1 /* Copyright (C) 2001 Marco d'Itri <md@linux.it>
2 * Copyright (C) 2001-2004 Andrew McDonald <andrew@mcdonald.org.uk>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #include <gnutls/gnutls.h>
24 #include <gnutls/x509.h>
25 #ifdef HAVE_GNUTLS_OPENSSL_H
26 #include <gnutls/openssl.h>
30 #include "mutt_socket.h"
31 #include "mutt_curses.h"
32 #include "mutt_menu.h"
34 #include "mutt_regex.h"
36 typedef struct _tlssockdata
39 gnutls_certificate_credentials xcred;
43 /* local prototypes */
44 static int tls_socket_read (CONNECTION* conn, char* buf, size_t len);
45 static int tls_socket_write (CONNECTION* conn, const char* buf, size_t len);
46 static int tls_socket_open (CONNECTION* conn);
47 static int tls_socket_close (CONNECTION* conn);
48 static int tls_starttls_close (CONNECTION* conn);
50 static int tls_init (void);
51 static int tls_negotiate (CONNECTION* conn);
52 static int tls_check_certificate (CONNECTION* conn);
55 static int tls_init (void)
57 static unsigned char init_complete = 0;
63 err = gnutls_global_init();
66 mutt_error ("gnutls_global_init: %s", gnutls_strerror(err));
75 int mutt_ssl_socket_setup (CONNECTION* conn)
80 conn->conn_open = tls_socket_open;
81 conn->conn_read = tls_socket_read;
82 conn->conn_write = tls_socket_write;
83 conn->conn_close = tls_socket_close;
84 conn->conn_poll = raw_socket_poll;
89 static int tls_socket_read (CONNECTION* conn, char* buf, size_t len)
91 tlssockdata *data = conn->sockdata;
96 mutt_error (_("Error: no TLS socket open"));
102 ret = gnutls_record_recv (data->state, buf, len);
103 if (ret < 0 && gnutls_error_is_fatal(ret) == 1)
105 mutt_error ("tls_socket_read (%s)", gnutls_strerror (ret));
110 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
115 static int tls_socket_write (CONNECTION* conn, const char* buf, size_t len)
117 tlssockdata *data = conn->sockdata;
123 mutt_error (_("Error: no TLS socket open"));
130 ret = gnutls_record_send (data->state, buf + sent, len - sent);
133 if (gnutls_error_is_fatal(ret) == 1)
135 mutt_error ("tls_socket_write (%s)", gnutls_strerror (ret));
142 } while (sent < len);
147 static int tls_socket_open (CONNECTION* conn)
149 if (raw_socket_open (conn) < 0)
152 if (tls_negotiate (conn) < 0)
154 tls_socket_close (conn);
161 int mutt_ssl_starttls (CONNECTION* conn)
166 if (tls_negotiate (conn) < 0)
169 conn->conn_read = tls_socket_read;
170 conn->conn_write = tls_socket_write;
171 conn->conn_close = tls_starttls_close;
176 static void tls_get_client_cert (CONNECTION* conn)
178 tlssockdata *data = conn->sockdata;
179 const gnutls_datum_t* crtdata;
180 gnutls_x509_crt_t clientcrt;
186 /* get our cert CN if we have one */
187 if (!(crtdata = gnutls_certificate_get_ours (data->state)))
190 if (gnutls_x509_crt_init (&clientcrt) < 0)
192 dprint (1, (debugfile, "Failed to init gnutls crt\n"));
195 if (gnutls_x509_crt_import (clientcrt, crtdata, GNUTLS_X509_FMT_DER) < 0)
197 dprint (1, (debugfile, "Failed to import gnutls client crt\n"));
200 /* get length of DN */
202 gnutls_x509_crt_get_dn (clientcrt, NULL, &dnlen);
203 if (!(dn = calloc (1, dnlen)))
205 dprint (1, (debugfile, "could not allocate DN\n"));
208 gnutls_x509_crt_get_dn (clientcrt, dn, &dnlen);
209 dprint (2, (debugfile, "client certificate DN: %s\n", dn));
211 /* extract CN to use as external user name */
212 if (!(cn = strstr (dn, "CN=")))
214 dprint (1, (debugfile, "no CN found in DN\n"));
219 if ((cnend = strstr (dn, ",EMAIL=")))
222 /* if we are using a client cert, SASL may expect an external auth name */
223 mutt_account_getuser (&conn->account);
228 gnutls_x509_crt_deinit (clientcrt);
231 static int protocol_priority[] = {GNUTLS_TLS1, GNUTLS_SSL3, 0};
233 /* tls_negotiate: After TLS state has been initialised, attempt to negotiate
234 * TLS over the wire, including certificate checks. */
235 static int tls_negotiate (CONNECTION * conn)
240 data = (tlssockdata *) safe_calloc (1, sizeof (tlssockdata));
241 conn->sockdata = data;
242 err = gnutls_certificate_allocate_credentials (&data->xcred);
245 FREE(&conn->sockdata);
246 mutt_error ("gnutls_certificate_allocate_credentials: %s", gnutls_strerror(err));
251 gnutls_certificate_set_x509_trust_file (data->xcred, SslCertFile,
252 GNUTLS_X509_FMT_PEM);
253 /* ignore errors, maybe file doesn't exist yet */
257 gnutls_certificate_set_x509_trust_file (data->xcred, SslCACertFile,
258 GNUTLS_X509_FMT_PEM);
263 dprint (2, (debugfile, "Using client certificate %s\n", SslClientCert));
264 gnutls_certificate_set_x509_key_file (data->xcred, SslClientCert,
265 SslClientCert, GNUTLS_X509_FMT_PEM);
268 gnutls_init(&data->state, GNUTLS_CLIENT);
271 gnutls_transport_set_ptr (data->state, (gnutls_transport_ptr)conn->fd);
273 /* disable TLS/SSL protocols as needed */
274 if (!option(OPTTLSV1) && !option(OPTSSLV3))
276 mutt_error (_("All available protocols for TLS/SSL connection disabled"));
279 else if (!option(OPTTLSV1))
281 protocol_priority[0] = GNUTLS_SSL3;
282 protocol_priority[1] = 0;
284 else if (!option(OPTSSLV3))
286 protocol_priority[0] = GNUTLS_TLS1;
287 protocol_priority[1] = 0;
291 use the list set above
294 /* We use default priorities (see gnutls documentation),
295 except for protocol version */
296 gnutls_set_default_priority (data->state);
297 gnutls_protocol_set_priority (data->state, protocol_priority);
299 if (SslDHPrimeBits > 0)
301 gnutls_dh_set_prime_bits (data->state, SslDHPrimeBits);
305 gnutls_set_cred (data->state, GNUTLS_ANON, NULL);
308 gnutls_credentials_set (data->state, GNUTLS_CRD_CERTIFICATE, data->xcred);
310 err = gnutls_handshake(data->state);
312 while (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED)
314 err = gnutls_handshake(data->state);
317 if (err == GNUTLS_E_FATAL_ALERT_RECEIVED)
319 mutt_error("gnutls_handshake: %s(%s)", gnutls_strerror(err),
320 gnutls_alert_get_name(gnutls_alert_get(data->state)));
324 mutt_error("gnutls_handshake: %s", gnutls_strerror(err));
330 if (!tls_check_certificate(conn))
333 /* set Security Strength Factor (SSF) for SASL */
334 /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
335 conn->ssf = gnutls_cipher_get_key_size (gnutls_cipher_get (data->state)) * 8;
337 tls_get_client_cert (conn);
339 if (!option(OPTNOCURSES)) {
340 mutt_message (_("SSL/TLS connection using %s (%s/%s/%s)"),
341 gnutls_protocol_get_name (gnutls_protocol_get_version (data->state)),
342 gnutls_kx_get_name (gnutls_kx_get (data->state)),
343 gnutls_cipher_get_name (gnutls_cipher_get (data->state)),
344 gnutls_mac_get_name (gnutls_mac_get (data->state)));
351 gnutls_certificate_free_credentials (data->xcred);
352 gnutls_deinit (data->state);
353 FREE(&conn->sockdata);
357 static int tls_socket_close (CONNECTION* conn)
359 tlssockdata *data = conn->sockdata;
362 gnutls_bye (data->state, GNUTLS_SHUT_RDWR);
364 gnutls_certificate_free_credentials (data->xcred);
365 gnutls_deinit (data->state);
366 FREE (&conn->sockdata);
369 return raw_socket_close (conn);
372 static int tls_starttls_close (CONNECTION* conn)
376 rc = tls_socket_close (conn);
377 conn->conn_read = raw_socket_read;
378 conn->conn_write = raw_socket_write;
379 conn->conn_close = raw_socket_close;
384 #define CERT_SEP "-----BEGIN"
386 /* this bit is based on read_ca_file() in gnutls */
387 static int tls_compare_certificates (const gnutls_datum *peercert)
393 gnutls_datum b64_data;
394 unsigned char *b64_data_data;
395 struct stat filestat;
397 if (stat(SslCertFile, &filestat) == -1)
400 b64_data.size = filestat.st_size+1;
401 b64_data_data = (unsigned char *) safe_calloc (1, b64_data.size);
402 b64_data_data[b64_data.size-1] = '\0';
403 b64_data.data = b64_data_data;
405 fd1 = fopen(SslCertFile, "r");
410 b64_data.size = fread(b64_data.data, 1, b64_data.size, fd1);
414 ret = gnutls_pem_base64_decode_alloc(NULL, &b64_data, &cert);
417 FREE (&b64_data_data);
421 ptr = (unsigned char *)strstr((char*)b64_data.data, CERT_SEP) + 1;
422 ptr = (unsigned char *)strstr((char*)ptr, CERT_SEP);
424 b64_data.size = b64_data.size - (ptr - b64_data.data);
427 if (cert.size == peercert->size)
429 if (memcmp (cert.data, peercert->data, cert.size) == 0)
432 gnutls_free(cert.data);
433 FREE (&b64_data_data);
438 gnutls_free(cert.data);
439 } while (ptr != NULL);
442 FREE (&b64_data_data);
446 static void tls_fingerprint (gnutls_digest_algorithm algo,
447 char* s, int l, const gnutls_datum* data)
449 unsigned char md[36];
455 if (gnutls_fingerprint (algo, data, (char *)md, &n) < 0)
457 snprintf (s, l, _("[unable to calculate]"));
461 for (j = 0; j < (int) n; j++)
464 snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
465 safe_strcat (s, l, ch);
467 s[2*n+n/2-1] = '\0'; /* don't want trailing space */
471 static char *tls_make_date (time_t t, char *s, size_t len)
473 struct tm *l = gmtime (&t);
476 snprintf (s, len, "%s, %d %s %d %02d:%02d:%02d UTC",
477 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
478 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec);
480 strfcpy (s, _("[invalid date]"), len);
485 static int tls_check_stored_hostname (const gnutls_datum *cert,
486 const char *hostname)
490 char *linestr = NULL;
494 regmatch_t pmatch[3];
496 /* try checking against names stored in stored certs file */
497 if ((fp = fopen (SslCertFile, "r")))
499 if (regcomp(&preg, "^#H ([a-zA-Z0-9_\\.-]+) ([0-9A-F]{4}( [0-9A-F]{4}){7})[ \t]*$", REG_ICASE|REG_EXTENDED) != 0)
506 tls_fingerprint (GNUTLS_DIG_MD5, buf, sizeof (buf), cert);
507 while ((linestr = mutt_read_line(linestr, &linestrsize, fp, &linenum)) != NULL)
509 if(linestr[0] == '#' && linestr[1] == 'H')
511 if (regexec(&preg, linestr, 3, pmatch, 0) == 0)
513 linestr[pmatch[1].rm_eo] = '\0';
514 linestr[pmatch[2].rm_eo] = '\0';
515 if (strcmp(linestr + pmatch[1].rm_so, hostname) == 0 &&
516 strcmp(linestr + pmatch[2].rm_so, buf) == 0)
531 /* not found a matching name */
535 static int tls_check_one_certificate (const gnutls_datum_t *certdata,
536 gnutls_certificate_status certstat,
537 const char* hostname, int idx, int len)
539 gnutls_x509_crt cert;
540 int certerr_hostname = 0;
541 int certerr_expired = 0;
542 int certerr_notyetvalid = 0;
543 int certerr_nottrusted = 0;
544 int certerr_revoked = 0;
545 int certerr_signernotca = 0;
546 char buf[SHORT_STRING];
547 char fpbuf[SHORT_STRING];
549 char dn_common_name[SHORT_STRING];
550 char dn_email[SHORT_STRING];
551 char dn_organization[SHORT_STRING];
552 char dn_organizational_unit[SHORT_STRING];
553 char dn_locality[SHORT_STRING];
554 char dn_province[SHORT_STRING];
555 char dn_country[SHORT_STRING];
559 char helpstr[LONG_STRING];
562 gnutls_datum pemdata;
563 int i, row, done, ret;
565 if (gnutls_x509_crt_init (&cert) < 0)
567 mutt_error (_("Error initialising gnutls certificate data"));
572 if (gnutls_x509_crt_import (cert, certdata, GNUTLS_X509_FMT_DER) < 0)
574 mutt_error (_("Error processing certificate data"));
576 gnutls_x509_crt_deinit (cert);
580 if (gnutls_x509_crt_get_expiration_time (cert) < time(NULL))
582 if (gnutls_x509_crt_get_activation_time (cert) > time(NULL))
583 certerr_notyetvalid = 1;
587 if (!gnutls_x509_crt_check_hostname (cert, hostname) &&
588 !tls_check_stored_hostname (certdata, hostname))
589 certerr_hostname = 1;
592 /* see whether certificate is in our cache (certificates file) */
593 if (tls_compare_certificates (certdata))
595 if (certstat & GNUTLS_CERT_INVALID)
597 /* doesn't matter - have decided is valid because server
598 certificate is in our trusted cache */
599 certstat ^= GNUTLS_CERT_INVALID;
602 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND)
604 /* doesn't matter that we haven't found the signer, since
605 certificate is in our trusted cache */
606 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
609 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA)
611 /* Hmm. Not really sure how to handle this, but let's say
612 that we don't care if the CA certificate hasn't got the
613 correct X.509 basic constraints if server certificate is
615 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
619 if (certstat & GNUTLS_CERT_REVOKED)
622 certstat ^= GNUTLS_CERT_REVOKED;
625 if (certstat & GNUTLS_CERT_INVALID)
627 certerr_nottrusted = 1;
628 certstat ^= GNUTLS_CERT_INVALID;
631 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND)
633 /* NB: already cleared if cert in cache */
634 certerr_nottrusted = 1;
635 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
638 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA)
640 /* NB: already cleared if cert in cache */
641 certerr_signernotca = 1;
642 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
645 /* OK if signed by (or is) a trusted certificate */
646 /* we've been zeroing the interesting bits in certstat -
647 don't return OK if there are any unhandled bits we don't
649 if (!(certerr_expired || certerr_notyetvalid ||
650 certerr_hostname || certerr_nottrusted) && certstat == 0)
652 gnutls_x509_crt_deinit (cert);
656 /* interactive check from user */
657 menu = mutt_new_menu (-1);
659 menu->dialog = (char **) safe_calloc (1, menu->max * sizeof (char *));
660 for (i = 0; i < menu->max; i++)
661 menu->dialog[i] = (char *) safe_calloc (1, SHORT_STRING * sizeof (char));
664 strfcpy (menu->dialog[row], _("This certificate belongs to:"), SHORT_STRING);
667 buflen = sizeof (dn_common_name);
668 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
669 dn_common_name, &buflen) != 0)
670 dn_common_name[0] = '\0';
671 buflen = sizeof (dn_email);
672 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
673 dn_email, &buflen) != 0)
675 buflen = sizeof (dn_organization);
676 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0,
677 dn_organization, &buflen) != 0)
678 dn_organization[0] = '\0';
679 buflen = sizeof (dn_organizational_unit);
680 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
681 dn_organizational_unit, &buflen) != 0)
682 dn_organizational_unit[0] = '\0';
683 buflen = sizeof (dn_locality);
684 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0,
685 dn_locality, &buflen) != 0)
686 dn_locality[0] = '\0';
687 buflen = sizeof (dn_province);
688 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0,
689 dn_province, &buflen) != 0)
690 dn_province[0] = '\0';
691 buflen = sizeof (dn_country);
692 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
693 dn_country, &buflen) != 0)
694 dn_country[0] = '\0';
696 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name, dn_email);
697 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization);
698 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organizational_unit);
699 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s",
700 dn_locality, dn_province, dn_country);
703 strfcpy (menu->dialog[row], _("This certificate was issued by:"), SHORT_STRING);
706 buflen = sizeof (dn_common_name);
707 if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
708 dn_common_name, &buflen) != 0)
709 dn_common_name[0] = '\0';
710 buflen = sizeof (dn_email);
711 if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
712 dn_email, &buflen) != 0)
714 buflen = sizeof (dn_organization);
715 if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0,
716 dn_organization, &buflen) != 0)
717 dn_organization[0] = '\0';
718 buflen = sizeof (dn_organizational_unit);
719 if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
720 dn_organizational_unit, &buflen) != 0)
721 dn_organizational_unit[0] = '\0';
722 buflen = sizeof (dn_locality);
723 if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0,
724 dn_locality, &buflen) != 0)
725 dn_locality[0] = '\0';
726 buflen = sizeof (dn_province);
727 if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0,
728 dn_province, &buflen) != 0)
729 dn_province[0] = '\0';
730 buflen = sizeof (dn_country);
731 if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
732 dn_country, &buflen) != 0)
733 dn_country[0] = '\0';
735 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name, dn_email);
736 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization);
737 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organizational_unit);
738 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s",
739 dn_locality, dn_province, dn_country);
742 snprintf (menu->dialog[row++], SHORT_STRING, _("This certificate is valid"));
744 t = gnutls_x509_crt_get_activation_time (cert);
745 snprintf (menu->dialog[row++], SHORT_STRING, _(" from %s"),
746 tls_make_date (t, datestr, 30));
748 t = gnutls_x509_crt_get_expiration_time (cert);
749 snprintf (menu->dialog[row++], SHORT_STRING, _(" to %s"),
750 tls_make_date (t, datestr, 30));
753 tls_fingerprint (GNUTLS_DIG_SHA, fpbuf, sizeof (fpbuf), certdata);
754 snprintf (menu->dialog[row++], SHORT_STRING, _("SHA1 Fingerprint: %s"), fpbuf);
756 tls_fingerprint (GNUTLS_DIG_MD5, fpbuf, sizeof (fpbuf), certdata);
757 snprintf (menu->dialog[row++], SHORT_STRING, _("MD5 Fingerprint: %s"), fpbuf);
759 if (certerr_notyetvalid)
762 strfcpy (menu->dialog[row], _("WARNING: Server certificate is not yet valid"), SHORT_STRING);
767 strfcpy (menu->dialog[row], _("WARNING: Server certificate has expired"), SHORT_STRING);
772 strfcpy (menu->dialog[row], _("WARNING: Server certificate has been revoked"), SHORT_STRING);
774 if (certerr_hostname)
777 strfcpy (menu->dialog[row], _("WARNING: Server hostname does not match certificate"), SHORT_STRING);
779 if (certerr_signernotca)
782 strfcpy (menu->dialog[row], _("WARNING: Signer of server certificate is not a CA"), SHORT_STRING);
785 snprintf (title, sizeof (title),
786 _("SSL Certificate check (certificate %d of %d in chain)"),
789 /* certificates with bad dates, or that are revoked, must be
790 accepted manually each and every time */
791 if (SslCertFile && !certerr_expired && !certerr_notyetvalid && !certerr_revoked)
793 menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
794 menu->keys = _("roa");
798 menu->prompt = _("(r)eject, accept (o)nce");
799 menu->keys = _("ro");
803 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_GENERIC, OP_EXIT);
804 safe_strcat (helpstr, sizeof (helpstr), buf);
805 mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
806 safe_strcat (helpstr, sizeof (helpstr), buf);
807 menu->help = helpstr;
810 set_option (OPTUNBUFFEREDINPUT);
813 switch (mutt_menuLoop (menu))
816 case OP_MAX + 1: /* reject */
820 case OP_MAX + 3: /* accept always */
822 if ((fp = fopen (SslCertFile, "a")))
824 /* save hostname if necessary */
825 if (certerr_hostname)
827 fprintf(fp, "#H %s %s\n", hostname, fpbuf);
830 if (certerr_nottrusted)
833 ret = gnutls_pem_base64_encode_alloc ("CERTIFICATE", certdata,
837 if (fwrite (pemdata.data, pemdata.size, 1, fp) == 1)
841 gnutls_free (pemdata.data);
848 mutt_error (_("Warning: Couldn't save certificate"));
853 mutt_message (_("Certificate saved"));
857 case OP_MAX + 2: /* accept once */
862 unset_option (OPTUNBUFFEREDINPUT);
863 mutt_menuDestroy (&menu);
864 gnutls_x509_crt_deinit (cert);
869 static int tls_check_certificate (CONNECTION* conn)
871 tlssockdata *data = conn->sockdata;
872 gnutls_session state = data->state;
873 const gnutls_datum *cert_list;
874 unsigned int cert_list_size = 0;
875 gnutls_certificate_status certstat;
878 if (gnutls_auth_get_type (state) != GNUTLS_CRD_CERTIFICATE)
880 mutt_error (_("Unable to get certificate from peer"));
885 certstat = gnutls_certificate_verify_peers (state);
887 if (certstat == GNUTLS_E_NO_CERTIFICATE_FOUND)
889 mutt_error (_("Unable to get certificate from peer"));
895 mutt_error (_("Certificate verification error (%s)"),
896 gnutls_strerror (certstat));
901 /* We only support X.509 certificates (not OpenPGP) at the moment */
902 if (gnutls_certificate_type_get (state) != GNUTLS_CRT_X509)
904 mutt_error (_("Certificate is not X.509"));
909 cert_list = gnutls_certificate_get_peers (state, &cert_list_size);
912 mutt_error (_("Unable to get certificate from peer"));
917 for (i = cert_list_size - 1; i >= 0; i--)
919 rc = tls_check_one_certificate (&cert_list[i], certstat, conn->account.host,