1 /**
2  * @file
3  * Handling of GnuTLS encryption
4  *
5  * @authors
6  * Copyright (C) 2001 Marco d'Itri <md@linux.it>
7  * Copyright (C) 2001-2004 Andrew McDonald <andrew@mcdonald.org.uk>
8  *
9  * @copyright
10  * This program is free software: you can redistribute it and/or modify it under
11  * the terms of the GNU General Public License as published by the Free Software
12  * Foundation, either version 2 of the License, or (at your option) any later
13  * version.
14  *
15  * This program is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License along with
21  * this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 /**
25  * @page conn_gnutls GnuTLS encryption
26  *
27  * Handling of GnuTLS encryption
28  */
29 
30 #include "config.h"
31 #include <gnutls/gnutls.h>
32 #include <gnutls/x509.h>
33 #include <stdbool.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <sys/stat.h>
37 #include "private.h"
38 #include "mutt/lib.h"
39 #include "config/lib.h"
40 #include "core/lib.h"
41 #include "lib.h"
42 #include "muttlib.h"
43 #include "options.h"
44 #include "ssl.h"
45 
46 // clang-format off
47 /* certificate error bitmap values */
48 #define CERTERR_VALID              0
49 #define CERTERR_EXPIRED      (1 << 0)
50 #define CERTERR_NOTYETVALID  (1 << 1)
51 #define CERTERR_REVOKED      (1 << 2)
52 #define CERTERR_NOTTRUSTED   (1 << 3)
53 #define CERTERR_HOSTNAME     (1 << 4)
54 #define CERTERR_SIGNERNOTCA  (1 << 5)
55 #define CERTERR_INSECUREALG  (1 << 6)
56 #define CERTERR_OTHER        (1 << 7)
57 // clang-format on
58 
59 const int dialog_row_len = 128;
60 
61 #define CERT_SEP "-----BEGIN"
62 
63 #ifndef HAVE_GNUTLS_PRIORITY_SET_DIRECT
64 /* This array needs to be large enough to hold all the possible values support
65  * by NeoMutt.  The initialized values are just placeholders--the array gets
66  * overwrriten in tls_negotiate() depending on the $ssl_use_* options.
67  *
68  * Note: gnutls_protocol_set_priority() was removed in GnuTLS version
69  * 3.4 (2015-04).  TLS 1.3 support wasn't added until version 3.6.5.
70  * Therefore, no attempt is made to support $ssl_use_tlsv1_3 in this code.
71  */
72 static int protocol_priority[] = { GNUTLS_TLS1_2, GNUTLS_TLS1_1, GNUTLS_TLS1,
73                                    GNUTLS_SSL3, 0 };
74 #endif
75 
76 /**
77  * struct TlsSockData - TLS socket data - @extends Connection
78  */
79 struct TlsSockData
80 {
81   gnutls_session_t state;
82   gnutls_certificate_credentials_t xcred;
83 };
84 
85 /**
86  * tls_init - Set up Gnu TLS
87  * @retval  0 Success
88  * @retval -1 Error
89  */
tls_init(void)90 static int tls_init(void)
91 {
92   static bool init_complete = false;
93   int err;
94 
95   if (init_complete)
96     return 0;
97 
98   err = gnutls_global_init();
99   if (err < 0)
100   {
101     mutt_error("gnutls_global_init: %s", gnutls_strerror(err));
102     return -1;
103   }
104 
105   init_complete = true;
106   return 0;
107 }
108 
109 /**
110  * tls_verify_peers - Wrapper for gnutls_certificate_verify_peers()
111  * @param tlsstate TLS state
112  * @param certstat Certificate state, e.g. GNUTLS_CERT_INVALID
113  * @retval  0 Success If certstat was set. note: this does not mean success
114  * @retval >0 Error
115  *
116  * Wrapper with sanity-checking.
117  *
118  * certstat is technically a bitwise-or of gnutls_certificate_status_t values.
119  */
tls_verify_peers(gnutls_session_t tlsstate,gnutls_certificate_status_t * certstat)120 static int tls_verify_peers(gnutls_session_t tlsstate, gnutls_certificate_status_t *certstat)
121 {
122   /* gnutls_certificate_verify_peers2() chains to
123    * gnutls_x509_trust_list_verify_crt2().  That function's documentation says:
124    *
125    *   When a certificate chain of cert_list_size with more than one
126    *   certificates is provided, the verification status will apply to
127    *   the first certificate in the chain that failed
128    *   verification. The verification process starts from the end of
129    *   the chain(from CA to end certificate). The first certificate
130    *   in the chain must be the end-certificate while the rest of the
131    *   members may be sorted or not.
132    *
133    * This is why tls_check_certificate() loops from CA to host in that order,
134    * calling the menu, and recalling tls_verify_peers() for each approved
135    * cert in the chain.
136    */
137   int rc = gnutls_certificate_verify_peers2(tlsstate, certstat);
138 
139   /* certstat was set */
140   if (rc == 0)
141     return 0;
142 
143   if (rc == GNUTLS_E_NO_CERTIFICATE_FOUND)
144     mutt_error(_("Unable to get certificate from peer"));
145   else
146     mutt_error(_("Certificate verification error (%s)"), gnutls_strerror(rc));
147 
148   return rc;
149 }
150 
151 /**
152  * tls_fingerprint - Create a fingerprint of a TLS Certificate
153  * @param algo   Fingerprint algorithm, e.g. GNUTLS_MAC_SHA256
154  * @param buf    Buffer for the fingerprint
155  * @param buflen Length of the buffer
156  * @param data Certificate
157  */
tls_fingerprint(gnutls_digest_algorithm_t algo,char * buf,size_t buflen,const gnutls_datum_t * data)158 static void tls_fingerprint(gnutls_digest_algorithm_t algo, char *buf,
159                             size_t buflen, const gnutls_datum_t *data)
160 {
161   unsigned char md[64];
162   size_t n;
163 
164   n = 64;
165 
166   if (gnutls_fingerprint(algo, data, (char *) md, &n) < 0)
167   {
168     snprintf(buf, buflen, _("[unable to calculate]"));
169   }
170   else
171   {
172     for (int i = 0; i < (int) n; i++)
173     {
174       char ch[8];
175       snprintf(ch, 8, "%02X%s", md[i], ((i % 2) ? " " : ""));
176       mutt_str_cat(buf, buflen, ch);
177     }
178     buf[2 * n + n / 2 - 1] = '\0'; /* don't want trailing space */
179   }
180 }
181 
182 /**
183  * tls_check_stored_hostname - Does the hostname match a stored certificate?
184  * @param cert     Certificate
185  * @param hostname Hostname
186  * @retval 1 Hostname match found
187  * @retval 0 Error, or no match
188  */
tls_check_stored_hostname(const gnutls_datum_t * cert,const char * hostname)189 static int tls_check_stored_hostname(const gnutls_datum_t *cert, const char *hostname)
190 {
191   char *linestr = NULL;
192   size_t linestrsize = 0;
193 
194   /* try checking against names stored in stored certs file */
195   const char *const c_certificate_file =
196       cs_subset_path(NeoMutt->sub, "certificate_file");
197   FILE *fp = mutt_file_fopen(c_certificate_file, "r");
198   if (!fp)
199     return 0;
200 
201   char buf[80];
202   buf[0] = '\0';
203   tls_fingerprint(GNUTLS_DIG_MD5, buf, sizeof(buf), cert);
204   while ((linestr = mutt_file_read_line(linestr, &linestrsize, fp, NULL, MUTT_RL_NO_FLAGS)))
205   {
206     regmatch_t *match = mutt_prex_capture(PREX_GNUTLS_CERT_HOST_HASH, linestr);
207     if (match)
208     {
209       regmatch_t *mhost = &match[PREX_GNUTLS_CERT_HOST_HASH_MATCH_HOST];
210       regmatch_t *mhash = &match[PREX_GNUTLS_CERT_HOST_HASH_MATCH_HASH];
211       linestr[mutt_regmatch_end(mhost)] = '\0';
212       linestr[mutt_regmatch_end(mhash)] = '\0';
213       if ((strcmp(linestr + mutt_regmatch_start(mhost), hostname) == 0) &&
214           (strcmp(linestr + mutt_regmatch_start(mhash), buf) == 0))
215       {
216         FREE(&linestr);
217         mutt_file_fclose(&fp);
218         return 1;
219       }
220     }
221   }
222 
223   mutt_file_fclose(&fp);
224 
225   /* not found a matching name */
226   return 0;
227 }
228 
229 /**
230  * tls_compare_certificates - Compare certificates against `$certificate_file`
231  * @param peercert Certificate
232  * @retval 1 Certificate matches file
233  * @retval 0 Error, or no match
234  */
tls_compare_certificates(const gnutls_datum_t * peercert)235 static int tls_compare_certificates(const gnutls_datum_t *peercert)
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 =
244       cs_subset_path(NeoMutt->sub, "certificate_file");
245   if (stat(c_certificate_file, &st) == -1)
246     return 0;
247 
248   b64_data.size = st.st_size;
249   b64_data_data = mutt_mem_calloc(1, b64_data.size + 1);
250   b64_data.data = b64_data_data;
251 
252   FILE *fp = mutt_file_fopen(c_certificate_file, "r");
253   if (!fp)
254     return 0;
255 
256   b64_data.size = fread(b64_data.data, 1, b64_data.size, fp);
257   mutt_file_fclose(&fp);
258 
259   do
260   {
261     const int ret = gnutls_pem_base64_decode_alloc(NULL, &b64_data, &cert);
262     if (ret != 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 }
300 
301 /**
302  * tls_check_preauth - Prepare a certificate for authentication
303  * @param[in]  certdata  List of GnuTLS certificates
304  * @param[in]  certstat  GnuTLS certificate status
305  * @param[in]  hostname  Hostname
306  * @param[in]  chainidx  Index in the certificate chain
307  * @param[out] certerr   Result, e.g. #CERTERR_VALID
308  * @param[out] savedcert 1 if certificate has been saved
309  * @retval  0 Success
310  * @retval -1 Error
311  */
tls_check_preauth(const gnutls_datum_t * certdata,gnutls_certificate_status_t certstat,const char * hostname,int chainidx,int * certerr,int * savedcert)312 static int tls_check_preauth(const gnutls_datum_t *certdata,
313                              gnutls_certificate_status_t certstat, const char *hostname,
314                              int chainidx, int *certerr, int *savedcert)
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 =
339       cs_subset_bool(NeoMutt->sub, "ssl_verify_dates");
340   if (c_ssl_verify_dates != MUTT_NO)
341   {
342     if (gnutls_x509_crt_get_expiration_time(cert) < mutt_date_epoch())
343       *certerr |= CERTERR_EXPIRED;
344     if (gnutls_x509_crt_get_activation_time(cert) > mutt_date_epoch())
345       *certerr |= CERTERR_NOTYETVALID;
346   }
347 
348   const bool c_ssl_verify_host =
349       cs_subset_bool(NeoMutt->sub, "ssl_verify_host");
350   if ((chainidx == 0) && (c_ssl_verify_host != MUTT_NO) &&
351       !gnutls_x509_crt_check_hostname(cert, hostname) &&
352       !tls_check_stored_hostname(certdata, hostname))
353   {
354     *certerr |= CERTERR_HOSTNAME;
355   }
356 
357   if (certstat & GNUTLS_CERT_REVOKED)
358   {
359     *certerr |= CERTERR_REVOKED;
360     certstat ^= GNUTLS_CERT_REVOKED;
361   }
362 
363   /* see whether certificate is in our cache (certificates file) */
364   if (tls_compare_certificates(certdata))
365   {
366     *savedcert = 1;
367 
368     /* We check above for certs with bad dates or that are revoked.
369      * These must be accepted manually each time.  Otherwise, we
370      * accept saved certificates as valid. */
371     if (*certerr == CERTERR_VALID)
372     {
373       gnutls_x509_crt_deinit(cert);
374       return 0;
375     }
376   }
377 
378   if (certstat & GNUTLS_CERT_INVALID)
379   {
380     *certerr |= CERTERR_NOTTRUSTED;
381     certstat ^= GNUTLS_CERT_INVALID;
382   }
383 
384   if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND)
385   {
386     /* NB: already cleared if cert in cache */
387     *certerr |= CERTERR_NOTTRUSTED;
388     certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
389   }
390 
391   if (certstat & GNUTLS_CERT_SIGNER_NOT_CA)
392   {
393     /* NB: already cleared if cert in cache */
394     *certerr |= CERTERR_SIGNERNOTCA;
395     certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
396   }
397 
398   if (certstat & GNUTLS_CERT_INSECURE_ALGORITHM)
399   {
400     /* NB: already cleared if cert in cache */
401     *certerr |= CERTERR_INSECUREALG;
402     certstat ^= GNUTLS_CERT_INSECURE_ALGORITHM;
403   }
404 
405   /* we've been zeroing the interesting bits in certstat -
406    * don't return OK if there are any unhandled bits we don't
407    * understand */
408   if (certstat != 0)
409     *certerr |= CERTERR_OTHER;
410 
411   gnutls_x509_crt_deinit(cert);
412 
413   if (*certerr == CERTERR_VALID)
414     return 0;
415 
416   return -1;
417 }
418 
419 /**
420  * add_cert - Look up certificate info and save it to a list
421  * @param title  Title for this block of certificate info
422  * @param cert   Certificate
423  * @param issuer If true, look up the issuer rather than owner details
424  * @param list   List to save info to
425  */
add_cert(const char * title,gnutls_x509_crt_t cert,bool issuer,struct ListHead * list)426 static void add_cert(const char *title, gnutls_x509_crt_t cert, bool issuer,
427                      struct ListHead *list)
428 {
429   static const char *part[] = {
430     GNUTLS_OID_X520_COMMON_NAME,              // CN
431     GNUTLS_OID_PKCS9_EMAIL,                   // Email
432     GNUTLS_OID_X520_ORGANIZATION_NAME,        // O
433     GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, // OU
434     GNUTLS_OID_X520_LOCALITY_NAME,            // L
435     GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME,   // ST
436     GNUTLS_OID_X520_COUNTRY_NAME,             // C
437   };
438 
439   char buf[128];
440   int rc;
441 
442   // Allocate formatted strings and let the ListHead take ownership
443   mutt_list_insert_tail(list, mutt_str_dup(title));
444 
445   for (size_t i = 0; i < mutt_array_size(part); i++)
446   {
447     size_t buflen = sizeof(buf);
448     if (issuer)
449       rc = gnutls_x509_crt_get_issuer_dn_by_oid(cert, part[i], 0, 0, buf, &buflen);
450     else
451       rc = gnutls_x509_crt_get_dn_by_oid(cert, part[i], 0, 0, buf, &buflen);
452     if (rc != 0)
453       continue;
454 
455     char *line = NULL;
456     mutt_str_asprintf(&line, "   %s", buf);
457     mutt_list_insert_tail(list, line);
458   }
459 }
460 
461 /**
462  * tls_check_one_certificate - Check a GnuTLS certificate
463  * @param certdata List of GnuTLS certificates
464  * @param certstat GnuTLS certificate status
465  * @param hostname Hostname
466  * @param idx      Index into certificate list
467  * @param len      Length of certificate list
468  * @retval 1 Success
469  * @retval 0 Failure
470  */
tls_check_one_certificate(const gnutls_datum_t * certdata,gnutls_certificate_status_t certstat,const char * hostname,int idx,size_t len)471 static int tls_check_one_certificate(const gnutls_datum_t *certdata,
472                                      gnutls_certificate_status_t certstat,
473                                      const char *hostname, int idx, size_t len)
474 {
475   struct ListHead list = STAILQ_HEAD_INITIALIZER(list);
476   int certerr, savedcert;
477   gnutls_x509_crt_t cert;
478   char fpbuf[128];
479   time_t t;
480   char datestr[30];
481   char title[256];
482   gnutls_datum_t pemdata = { 0 };
483 
484   if (tls_check_preauth(certdata, certstat, hostname, idx, &certerr, &savedcert) == 0)
485     return 1;
486 
487   /* interactive check from user */
488   if (gnutls_x509_crt_init(&cert) < 0)
489   {
490     mutt_error(_("Error initialising gnutls certificate data"));
491     return 0;
492   }
493 
494   if (gnutls_x509_crt_import(cert, certdata, GNUTLS_X509_FMT_DER) < 0)
495   {
496     mutt_error(_("Error processing certificate data"));
497     gnutls_x509_crt_deinit(cert);
498     return 0;
499   }
500 
501   add_cert(_("This certificate belongs to:"), cert, false, &list);
502   mutt_list_insert_tail(&list, NULL);
503   add_cert(_("This certificate was issued by:"), cert, true, &list);
504 
505   mutt_list_insert_tail(&list, NULL);
506   mutt_list_insert_tail(&list, mutt_str_dup(_("This certificate is valid")));
507 
508   char *line = NULL;
509   t = gnutls_x509_crt_get_activation_time(cert);
510   mutt_date_make_tls(datestr, sizeof(datestr), t);
511   mutt_str_asprintf(&line, _("   from %s"), datestr);
512   mutt_list_insert_tail(&list, line);
513 
514   t = gnutls_x509_crt_get_expiration_time(cert);
515   mutt_date_make_tls(datestr, sizeof(datestr), t);
516   mutt_str_asprintf(&line, _("     to %s"), datestr);
517   mutt_list_insert_tail(&list, line);
518   mutt_list_insert_tail(&list, NULL);
519 
520   fpbuf[0] = '\0';
521   tls_fingerprint(GNUTLS_DIG_SHA, fpbuf, sizeof(fpbuf), certdata);
522   mutt_str_asprintf(&line, _("SHA1 Fingerprint: %s"), fpbuf);
523   mutt_list_insert_tail(&list, line);
524   fpbuf[0] = '\0';
525   fpbuf[40] = '\0'; /* Ensure the second printed line is null terminated */
526   tls_fingerprint(GNUTLS_DIG_SHA256, fpbuf, sizeof(fpbuf), certdata);
527   fpbuf[39] = '\0'; /* Divide into two lines of output */
528   mutt_str_asprintf(&line, "%s%s", _("SHA256 Fingerprint: "), fpbuf);
529   mutt_list_insert_tail(&list, line);
530   mutt_str_asprintf(&line, "%*s%s", (int) mutt_str_len(_("SHA256 Fingerprint: ")),
531                     "", fpbuf + 40);
532   mutt_list_insert_tail(&list, line);
533 
534   if (certerr)
535     mutt_list_insert_tail(&list, NULL);
536 
537   if (certerr & CERTERR_NOTYETVALID)
538   {
539     mutt_list_insert_tail(
540         &list, mutt_str_dup(_("WARNING: Server certificate is not yet valid")));
541   }
542   if (certerr & CERTERR_EXPIRED)
543   {
544     mutt_list_insert_tail(
545         &list, mutt_str_dup(_("WARNING: Server certificate has expired")));
546   }
547   if (certerr & CERTERR_REVOKED)
548   {
549     mutt_list_insert_tail(
550         &list, mutt_str_dup(_("WARNING: Server certificate has been revoked")));
551   }
552   if (certerr & CERTERR_HOSTNAME)
553   {
554     mutt_list_insert_tail(
555         &list,
556         mutt_str_dup(_("WARNING: Server hostname does not match certificate")));
557   }
558   if (certerr & CERTERR_SIGNERNOTCA)
559   {
560     mutt_list_insert_tail(
561         &list,
562         mutt_str_dup(_("WARNING: Signer of server certificate is not a CA")));
563   }
564   if (certerr & CERTERR_INSECUREALG)
565   {
566     mutt_list_insert_tail(
567         &list, mutt_str_dup(_("Warning: Server certificate was signed using "
568                               "an insecure algorithm")));
569   }
570 
571   snprintf(title, sizeof(title),
572            _("SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len);
573 
574   const char *const c_certificate_file =
575       cs_subset_path(NeoMutt->sub, "certificate_file");
576   const bool allow_always =
577       (c_certificate_file && !savedcert &&
578        !(certerr & (CERTERR_EXPIRED | CERTERR_NOTYETVALID | CERTERR_REVOKED)));
579   int rc = dlg_verify_certificate(title, &list, allow_always, false);
580   if (rc == 3) // Accept always
581   {
582     bool saved = false;
583     FILE *fp = mutt_file_fopen(c_certificate_file, "a");
584     if (fp)
585     {
586       if (certerr & CERTERR_HOSTNAME) // Save hostname if necessary
587       {
588         fpbuf[0] = '\0';
589         tls_fingerprint(GNUTLS_DIG_MD5, fpbuf, sizeof(fpbuf), certdata);
590         fprintf(fp, "#H %s %s\n", hostname, fpbuf);
591         saved = true;
592       }
593       if (certerr ^ CERTERR_HOSTNAME) // Save the cert for all other errors
594       {
595         int rc2 = gnutls_pem_base64_encode_alloc("CERTIFICATE", certdata, &pemdata);
596         if (rc2 == 0)
597         {
598           if (fwrite(pemdata.data, pemdata.size, 1, fp) == 1)
599           {
600             saved = true;
601           }
602           gnutls_free(pemdata.data);
603         }
604       }
605       mutt_file_fclose(&fp);
606     }
607     if (!saved)
608       mutt_message(_("Certificate saved"));
609     else
610       mutt_error(_("Warning: Couldn't save certificate"));
611   }
612 
613   mutt_list_free(&list);
614   gnutls_x509_crt_deinit(cert);
615   return (rc > 1);
616 }
617 
618 /**
619  * tls_check_certificate - Check a connection's certificate
620  * @param conn Connection to a server
621  * @retval 1 Certificate is valid
622  * @retval 0 Error, or certificate is invalid
623  */
tls_check_certificate(struct Connection * conn)624 static int tls_check_certificate(struct Connection *conn)
625 {
626   struct TlsSockData *data = conn->sockdata;
627   gnutls_session_t state = data->state;
628   const gnutls_datum_t *cert_list = NULL;
629   unsigned int cert_list_size = 0;
630   gnutls_certificate_status_t certstat;
631   int certerr, savedcert, rc = 0;
632   int max_preauth_pass = -1;
633 
634   /* tls_verify_peers() calls gnutls_certificate_verify_peers2(),
635    * which verifies the auth_type is GNUTLS_CRD_CERTIFICATE
636    * and that get_certificate_type() for the server is GNUTLS_CRT_X509.
637    * If it returns 0, certstat will be set with failure codes for the first
638    * cert in the chain(from CA to host) with an error.
639    */
640   if (tls_verify_peers(state, &certstat) != 0)
641     return 0;
642 
643   cert_list = gnutls_certificate_get_peers(state, &cert_list_size);
644   if (!cert_list)
645   {
646     mutt_error(_("Unable to get certificate from peer"));
647     return 0;
648   }
649 
650   /* tls_verify_peers doesn't check hostname or expiration, so walk
651    * from most specific to least checking these. If we see a saved certificate,
652    * its status short-circuits the remaining checks. */
653   int preauthrc = 0;
654   for (int i = 0; i < cert_list_size; i++)
655   {
656     rc = tls_check_preauth(&cert_list[i], certstat, conn->account.host, i,
657                            &certerr, &savedcert);
658     preauthrc += rc;
659     if (!preauthrc)
660       max_preauth_pass = i;
661 
662     if (savedcert)
663     {
664       if (preauthrc == 0)
665         return 1;
666       break;
667     }
668   }
669 
670   /* then check interactively, starting from chain root */
671   for (int i = cert_list_size - 1; i >= 0; i--)
672   {
673     rc = tls_check_one_certificate(&cert_list[i], certstat, conn->account.host,
674                                    i, cert_list_size);
675 
676     /* Stop checking if the menu cert is aborted or rejected. */
677     if (rc == 0)
678       break;
679 
680     /* add signers to trust set, then reverify */
681     if (i)
682     {
683       int rcsettrust = gnutls_certificate_set_x509_trust_mem(
684           data->xcred, &cert_list[i], GNUTLS_X509_FMT_DER);
685       if (rcsettrust != 1)
686         mutt_debug(LL_DEBUG1, "error trusting certificate %d: %d\n", i, rcsettrust);
687 
688       if (tls_verify_peers(state, &certstat) != 0)
689         return 0;
690 
691       /* If the cert chain now verifies, and all lower certs already
692        * passed preauth, we are done. */
693       if (!certstat && (max_preauth_pass >= (i - 1)))
694         return 1;
695     }
696   }
697 
698   return rc;
699 }
700 
701 /**
702  * tls_get_client_cert - Get the client certificate for a TLS connection
703  * @param conn Connection to a server
704  *
705  * @note This function grabs the CN out of the client cert but appears to do
706  *       nothing with it.  It does contain a call to mutt_account_getuser().
707  */
tls_get_client_cert(struct Connection * conn)708 static void tls_get_client_cert(struct Connection *conn)
709 {
710   struct TlsSockData *data = conn->sockdata;
711   gnutls_x509_crt_t clientcrt;
712   char *cn = NULL;
713   size_t cnlen = 0;
714   int rc;
715 
716   /* get our cert CN if we have one */
717   const gnutls_datum_t *crtdata = gnutls_certificate_get_ours(data->state);
718   if (!crtdata)
719     return;
720 
721   if (gnutls_x509_crt_init(&clientcrt) < 0)
722   {
723     mutt_debug(LL_DEBUG1, "Failed to init gnutls crt\n");
724     return;
725   }
726 
727   if (gnutls_x509_crt_import(clientcrt, crtdata, GNUTLS_X509_FMT_DER) < 0)
728   {
729     mutt_debug(LL_DEBUG1, "Failed to import gnutls client crt\n");
730     goto err;
731   }
732 
733   /* get length of CN, then grab it. */
734   rc = gnutls_x509_crt_get_dn_by_oid(clientcrt, GNUTLS_OID_X520_COMMON_NAME, 0,
735                                      0, NULL, &cnlen);
736   if (((rc >= 0) || (rc == GNUTLS_E_SHORT_MEMORY_BUFFER)) && (cnlen > 0))
737   {
738     cn = mutt_mem_calloc(1, cnlen);
739     if (gnutls_x509_crt_get_dn_by_oid(clientcrt, GNUTLS_OID_X520_COMMON_NAME, 0,
740                                       0, cn, &cnlen) < 0)
741     {
742       goto err;
743     }
744     mutt_debug(LL_DEBUG2, "client certificate CN: %s\n", cn);
745 
746     /* if we are using a client cert, SASL may expect an external auth name */
747     if (mutt_account_getuser(&conn->account) < 0)
748       mutt_debug(LL_DEBUG1, "Couldn't get user info\n");
749   }
750 
751 err:
752   FREE(&cn);
753   gnutls_x509_crt_deinit(clientcrt);
754 }
755 
756 #ifdef HAVE_GNUTLS_PRIORITY_SET_DIRECT
757 /**
758  * tls_set_priority - Set TLS algorithm priorities
759  * @param data TLS socket data
760  * @retval  0 Success
761  * @retval -1 Error
762  */
tls_set_priority(struct TlsSockData * data)763 static int tls_set_priority(struct TlsSockData *data)
764 {
765   size_t nproto = 5;
766   int rv = -1;
767 
768   struct Buffer *priority = mutt_buffer_pool_get();
769 
770   const char *const c_ssl_ciphers =
771       cs_subset_string(NeoMutt->sub, "ssl_ciphers");
772   if (c_ssl_ciphers)
773     mutt_buffer_strcpy(priority, c_ssl_ciphers);
774   else
775     mutt_buffer_strcpy(priority, "NORMAL");
776 
777   const bool c_ssl_use_tlsv1_3 =
778       cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_3");
779   if (!c_ssl_use_tlsv1_3)
780   {
781     nproto--;
782     mutt_buffer_addstr(priority, ":-VERS-TLS1.3");
783   }
784   const bool c_ssl_use_tlsv1_2 =
785       cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_2");
786   if (!c_ssl_use_tlsv1_2)
787   {
788     nproto--;
789     mutt_buffer_addstr(priority, ":-VERS-TLS1.2");
790   }
791   const bool c_ssl_use_tlsv1_1 =
792       cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_1");
793   if (!c_ssl_use_tlsv1_1)
794   {
795     nproto--;
796     mutt_buffer_addstr(priority, ":-VERS-TLS1.1");
797   }
798   const bool c_ssl_use_tlsv1 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1");
799   if (!c_ssl_use_tlsv1)
800   {
801     nproto--;
802     mutt_buffer_addstr(priority, ":-VERS-TLS1.0");
803   }
804   const bool c_ssl_use_sslv3 = cs_subset_bool(NeoMutt->sub, "ssl_use_sslv3");
805   if (!c_ssl_use_sslv3)
806   {
807     nproto--;
808     mutt_buffer_addstr(priority, ":-VERS-SSL3.0");
809   }
810 
811   if (nproto == 0)
812   {
813     mutt_error(_("All available protocols for TLS/SSL connection disabled"));
814     goto cleanup;
815   }
816 
817   int err = gnutls_priority_set_direct(data->state, mutt_buffer_string(priority), NULL);
818   if (err < 0)
819   {
820     mutt_error("gnutls_priority_set_direct(%s): %s",
821                mutt_buffer_string(priority), gnutls_strerror(err));
822     goto cleanup;
823   }
824 
825   rv = 0;
826 
827 cleanup:
828   mutt_buffer_pool_release(&priority);
829   return rv;
830 }
831 
832 #else
833 /**
834  * tls_set_priority - Set the priority of various protocols
835  * @param data TLS socket data
836  * @retval  0 Success
837  * @retval -1 Error
838  */
tls_set_priority(struct TlsSockData * data)839 static int tls_set_priority(struct TlsSockData *data)
840 {
841   size_t nproto = 0; /* number of tls/ssl protocols */
842 
843   const bool c_ssl_use_tlsv1_2 =
844       cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_2");
845   if (c_ssl_use_tlsv1_2)
846     protocol_priority[nproto++] = GNUTLS_TLS1_2;
847   const bool c_ssl_use_tlsv1_1 =
848       cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_1");
849   if (c_ssl_use_tlsv1_1)
850     protocol_priority[nproto++] = GNUTLS_TLS1_1;
851   const bool c_ssl_use_tlsv1 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1");
852   if (c_ssl_use_tlsv1)
853     protocol_priority[nproto++] = GNUTLS_TLS1;
854   const bool c_ssl_use_sslv3 = cs_subset_bool(NeoMutt->sub, "ssl_use_sslv3");
855   if (c_ssl_use_sslv3)
856     protocol_priority[nproto++] = GNUTLS_SSL3;
857   protocol_priority[nproto] = 0;
858 
859   if (nproto == 0)
860   {
861     mutt_error(_("All available protocols for TLS/SSL connection disabled"));
862     return -1;
863   }
864 
865   const char *const c_ssl_ciphers =
866       cs_subset_string(NeoMutt->sub, "ssl_ciphers");
867   if (c_ssl_ciphers)
868   {
869     mutt_error(
870         _("Explicit ciphersuite selection via $ssl_ciphers not supported"));
871   }
872 
873   /* We use default priorities (see gnutls documentation),
874    * except for protocol version */
875   gnutls_set_default_priority(data->state);
876   gnutls_protocol_set_priority(data->state, protocol_priority);
877   return 0;
878 }
879 #endif
880 
881 /**
882  * tls_negotiate - Negotiate TLS connection
883  * @param conn Connection to a server
884  * @retval  0 Success
885  * @retval -1 Error
886  *
887  * After TLS state has been initialized, attempt to negotiate TLS over the
888  * wire, including certificate checks.
889  */
tls_negotiate(struct Connection * conn)890 static int tls_negotiate(struct Connection *conn)
891 {
892   struct TlsSockData *data = mutt_mem_calloc(1, sizeof(struct TlsSockData));
893   conn->sockdata = data;
894   int err = gnutls_certificate_allocate_credentials(&data->xcred);
895   if (err < 0)
896   {
897     FREE(&conn->sockdata);
898     mutt_error("gnutls_certificate_allocate_credentials: %s", gnutls_strerror(err));
899     return -1;
900   }
901 
902   const char *const c_certificate_file =
903       cs_subset_path(NeoMutt->sub, "certificate_file");
904   gnutls_certificate_set_x509_trust_file(data->xcred, c_certificate_file, GNUTLS_X509_FMT_PEM);
905   /* ignore errors, maybe file doesn't exist yet */
906 
907   const char *const c_ssl_ca_certificates_file =
908       cs_subset_path(NeoMutt->sub, "ssl_ca_certificates_file");
909   if (c_ssl_ca_certificates_file)
910   {
911     gnutls_certificate_set_x509_trust_file(data->xcred, c_ssl_ca_certificates_file,
912                                            GNUTLS_X509_FMT_PEM);
913   }
914 
915   const char *const c_ssl_client_cert =
916       cs_subset_path(NeoMutt->sub, "ssl_client_cert");
917   if (c_ssl_client_cert)
918   {
919     mutt_debug(LL_DEBUG2, "Using client certificate %s\n", c_ssl_client_cert);
920     gnutls_certificate_set_x509_key_file(data->xcred, c_ssl_client_cert,
921                                          c_ssl_client_cert, GNUTLS_X509_FMT_PEM);
922   }
923 
924 #ifdef HAVE_DECL_GNUTLS_VERIFY_DISABLE_TIME_CHECKS
925   /* disable checking certificate activation/expiration times
926    * in gnutls, we do the checks ourselves */
927   gnutls_certificate_set_verify_flags(data->xcred, GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
928 #endif
929 
930   err = gnutls_init(&data->state, GNUTLS_CLIENT);
931   if (err)
932   {
933     mutt_error("gnutls_init: %s", gnutls_strerror(err));
934     goto fail;
935   }
936 
937   /* set socket */
938   gnutls_transport_set_ptr(data->state, (gnutls_transport_ptr_t) (long) conn->fd);
939 
940   if (gnutls_server_name_set(data->state, GNUTLS_NAME_DNS, conn->account.host,
941                              mutt_str_len(conn->account.host)))
942   {
943     mutt_error(_("Warning: unable to set TLS SNI host name"));
944   }
945 
946   if (tls_set_priority(data) < 0)
947   {
948     goto fail;
949   }
950 
951   const short c_ssl_min_dh_prime_bits =
952       cs_subset_number(NeoMutt->sub, "ssl_min_dh_prime_bits");
953   if (c_ssl_min_dh_prime_bits > 0)
954   {
955     gnutls_dh_set_prime_bits(data->state, c_ssl_min_dh_prime_bits);
956   }
957 
958   gnutls_credentials_set(data->state, GNUTLS_CRD_CERTIFICATE, data->xcred);
959 
960   do
961   {
962     err = gnutls_handshake(data->state);
963   } while ((err == GNUTLS_E_AGAIN) || (err == GNUTLS_E_INTERRUPTED));
964 
965   if (err < 0)
966   {
967     if (err == GNUTLS_E_FATAL_ALERT_RECEIVED)
968     {
969       mutt_error("gnutls_handshake: %s(%s)", gnutls_strerror(err),
970                  gnutls_alert_get_name(gnutls_alert_get(data->state)));
971     }
972     else
973     {
974       mutt_error("gnutls_handshake: %s", gnutls_strerror(err));
975     }
976     goto fail;
977   }
978 
979   if (tls_check_certificate(conn) == 0)
980     goto fail;
981 
982   /* set Security Strength Factor (SSF) for SASL */
983   /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
984   conn->ssf = gnutls_cipher_get_key_size(gnutls_cipher_get(data->state)) * 8;
985 
986   tls_get_client_cert(conn);
987 
988   if (!OptNoCurses)
989   {
990     mutt_message(_("SSL/TLS connection using %s (%s/%s/%s)"),
991                  gnutls_protocol_get_name(gnutls_protocol_get_version(data->state)),
992                  gnutls_kx_get_name(gnutls_kx_get(data->state)),
993                  gnutls_cipher_get_name(gnutls_cipher_get(data->state)),
994                  gnutls_mac_get_name(gnutls_mac_get(data->state)));
995     mutt_sleep(0);
996   }
997 
998   return 0;
999 
1000 fail:
1001   gnutls_certificate_free_credentials(data->xcred);
1002   gnutls_deinit(data->state);
1003   FREE(&conn->sockdata);
1004   return -1;
1005 }
1006 
1007 /**
1008  * tls_socket_poll - Check whether a socket read would block - Implements Connection::poll() - @ingroup connection_poll
1009  */
tls_socket_poll(struct Connection * conn,time_t wait_secs)1010 static int tls_socket_poll(struct Connection *conn, time_t wait_secs)
1011 {
1012   struct TlsSockData *data = conn->sockdata;
1013   if (!data)
1014     return -1;
1015 
1016   if (gnutls_record_check_pending(data->state))
1017     return 1;
1018 
1019   return raw_socket_poll(conn, wait_secs);
1020 }
1021 
1022 /**
1023  * tls_socket_close - Close a TLS socket - Implements Connection::close() - @ingroup connection_close
1024  */
tls_socket_close(struct Connection * conn)1025 static int tls_socket_close(struct Connection *conn)
1026 {
1027   struct TlsSockData *data = conn->sockdata;
1028   if (data)
1029   {
1030     /* shut down only the write half to avoid hanging waiting for the remote to respond.
1031      *
1032      * RFC5246 7.2.1. "Closure Alerts"
1033      *
1034      * It is not required for the initiator of the close to wait for the
1035      * responding close_notify alert before closing the read side of the
1036      * connection.  */
1037     gnutls_bye(data->state, GNUTLS_SHUT_WR);
1038 
1039     gnutls_certificate_free_credentials(data->xcred);
1040     gnutls_deinit(data->state);
1041     FREE(&conn->sockdata);
1042   }
1043 
1044   return raw_socket_close(conn);
1045 }
1046 
1047 /**
1048  * tls_socket_open - Open a TLS socket - Implements Connection::open() - @ingroup connection_open
1049  */
tls_socket_open(struct Connection * conn)1050 static int tls_socket_open(struct Connection *conn)
1051 {
1052   if (raw_socket_open(conn) < 0)
1053     return -1;
1054 
1055   if (tls_negotiate(conn) < 0)
1056   {
1057     tls_socket_close(conn);
1058     return -1;
1059   }
1060 
1061   return 0;
1062 }
1063 
1064 /**
1065  * tls_socket_read - Read data from a TLS socket - Implements Connection::read() - @ingroup connection_read
1066  */
tls_socket_read(struct Connection * conn,char * buf,size_t count)1067 static int tls_socket_read(struct Connection *conn, char *buf, size_t count)
1068 {
1069   struct TlsSockData *data = conn->sockdata;
1070   if (!data)
1071   {
1072     mutt_error(_("Error: no TLS socket open"));
1073     return -1;
1074   }
1075 
1076   int rc;
1077   do
1078   {
1079     rc = gnutls_record_recv(data->state, buf, count);
1080   } while ((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED));
1081 
1082   if (rc < 0)
1083   {
1084     mutt_error("tls_socket_read (%s)", gnutls_strerror(rc));
1085     return -1;
1086   }
1087 
1088   return rc;
1089 }
1090 
1091 /**
1092  * tls_socket_write - Write data to a TLS socket - Implements Connection::write() - @ingroup connection_write
1093  */
tls_socket_write(struct Connection * conn,const char * buf,size_t count)1094 static int tls_socket_write(struct Connection *conn, const char *buf, size_t count)
1095 {
1096   struct TlsSockData *data = conn->sockdata;
1097   size_t sent = 0;
1098 
1099   if (!data)
1100   {
1101     mutt_error(_("Error: no TLS socket open"));
1102     return -1;
1103   }
1104 
1105   do
1106   {
1107     int ret;
1108     do
1109     {
1110       ret = gnutls_record_send(data->state, buf + sent, count - sent);
1111     } while ((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED));
1112 
1113     if (ret < 0)
1114     {
1115       mutt_error("tls_socket_write (%s)", gnutls_strerror(ret));
1116       return -1;
1117     }
1118 
1119     sent += ret;
1120   } while (sent < count);
1121 
1122   return sent;
1123 }
1124 
1125 /**
1126  * tls_starttls_close - Close a TLS connection - Implements Connection::close() - @ingroup connection_close
1127  */
tls_starttls_close(struct Connection * conn)1128 static int tls_starttls_close(struct Connection *conn)
1129 {
1130   int rc;
1131 
1132   rc = tls_socket_close(conn);
1133   conn->read = raw_socket_read;
1134   conn->write = raw_socket_write;
1135   conn->close = raw_socket_close;
1136   conn->poll = raw_socket_poll;
1137 
1138   return rc;
1139 }
1140 
1141 /**
1142  * mutt_ssl_socket_setup - Set up SSL socket mulitplexor
1143  * @param conn Connection to a server
1144  * @retval  0 Success
1145  * @retval -1 Error
1146  */
mutt_ssl_socket_setup(struct Connection * conn)1147 int mutt_ssl_socket_setup(struct Connection *conn)
1148 {
1149   if (tls_init() < 0)
1150     return -1;
1151 
1152   conn->open = tls_socket_open;
1153   conn->read = tls_socket_read;
1154   conn->write = tls_socket_write;
1155   conn->close = tls_socket_close;
1156   conn->poll = tls_socket_poll;
1157 
1158   return 0;
1159 }
1160 
1161 /**
1162  * mutt_ssl_starttls - Negotiate TLS over an already opened connection
1163  * @param conn Connection to a server
1164  * @retval  0 Success
1165  * @retval -1 Error
1166  */
mutt_ssl_starttls(struct Connection * conn)1167 int mutt_ssl_starttls(struct Connection *conn)
1168 {
1169   if (tls_init() < 0)
1170     return -1;
1171 
1172   if (tls_negotiate(conn) < 0)
1173     return -1;
1174 
1175   conn->read = tls_socket_read;
1176   conn->write = tls_socket_write;
1177   conn->close = tls_starttls_close;
1178   conn->poll = tls_socket_poll;
1179 
1180   return 0;
1181 }
1182