示例#1
0
static int cert_callback(gnutls_session_t session)
{
	const gnutls_datum_t *cert_list;
	unsigned int cert_list_size = 0;
	int ret;
	unsigned i;
	gnutls_datum_t t;
	struct priv_st *priv;

	cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
	if (cert_list_size == 0) {
		fprintf(stderr, "no certificates sent by server!\n");
		return -1;
	}

	priv = gnutls_session_get_ptr(session);

	for (i=0;i<cert_list_size;i++) {
		ret = gnutls_pem_base64_encode_alloc("CERTIFICATE", &cert_list[i], &t);
		if (ret < 0) {
			fprintf(stderr, "error[%d]: %s\n", __LINE__,
				gnutls_strerror(ret));
			exit(1);
		}

		write(priv->fd, t.data, t.size);
		gnutls_free(t.data);
	}
	priv->found = 1;

	return -1;
}
示例#2
0
static int tls_check_one_certificate (const gnutls_datum_t *certdata,
                                      gnutls_certificate_status certstat,
                                      const char* hostname, int idx, int len)
{
  int certerr, savedcert;
  gnutls_x509_crt cert;
  char buf[SHORT_STRING];
  char fpbuf[SHORT_STRING];
  size_t buflen;
  char dn_common_name[SHORT_STRING];
  char dn_email[SHORT_STRING];
  char dn_organization[SHORT_STRING];
  char dn_organizational_unit[SHORT_STRING];
  char dn_locality[SHORT_STRING];
  char dn_province[SHORT_STRING];
  char dn_country[SHORT_STRING];
  time_t t;
  char datestr[30];
  MUTTMENU *menu;
  char helpstr[LONG_STRING];
  char title[STRING];
  FILE *fp;
  gnutls_datum pemdata;
  int i, row, done, ret;

  if (!tls_check_preauth (certdata, certstat, hostname, idx, &certerr,
      &savedcert))
    return 1;

  /* skip signers if insecure algorithm was used */
  if (idx && (certerr & CERTERR_INSECUREALG))
  {
    if (idx == 1)
    {
      mutt_error (_("Warning: Server certificate was signed using an insecure algorithm"));
      mutt_sleep (2);
    }
    return 0;
  }

  /* interactive check from user */
  if (gnutls_x509_crt_init (&cert) < 0)
  {
    mutt_error (_("Error initialising gnutls certificate data"));
    mutt_sleep (2);
    return 0;
  }

  if (gnutls_x509_crt_import (cert, certdata, GNUTLS_X509_FMT_DER) < 0)
  {
    mutt_error (_("Error processing certificate data"));
    mutt_sleep (2);
    gnutls_x509_crt_deinit (cert);
    return -1;
  }

  menu = mutt_new_menu (-1);
  menu->max = 25;
  menu->dialog = (char **) safe_calloc (1, menu->max * sizeof (char *));
  for (i = 0; i < menu->max; i++)
    menu->dialog[i] = (char *) safe_calloc (1, SHORT_STRING * sizeof (char));

  row = 0;
  strfcpy (menu->dialog[row], _("This certificate belongs to:"), SHORT_STRING);
  row++;

  buflen = sizeof (dn_common_name);
  if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
                                     dn_common_name, &buflen) != 0)
    dn_common_name[0] = '\0';
  buflen = sizeof (dn_email);
  if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
                                     dn_email, &buflen) != 0)
    dn_email[0] = '\0';
  buflen = sizeof (dn_organization);
  if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0,
                                     dn_organization, &buflen) != 0)
    dn_organization[0] = '\0';
  buflen = sizeof (dn_organizational_unit);
  if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
                                     dn_organizational_unit, &buflen) != 0)
    dn_organizational_unit[0] = '\0';
  buflen = sizeof (dn_locality);
  if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0,
                                     dn_locality, &buflen) != 0)
    dn_locality[0] = '\0';
  buflen = sizeof (dn_province);
  if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0,
                                     dn_province, &buflen) != 0)
    dn_province[0] = '\0';
  buflen = sizeof (dn_country);
  if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
                                     dn_country, &buflen) != 0)
    dn_country[0] = '\0';

  snprintf (menu->dialog[row++], SHORT_STRING, "   %s  %s", dn_common_name, dn_email);
  snprintf (menu->dialog[row++], SHORT_STRING, "   %s", dn_organization);
  snprintf (menu->dialog[row++], SHORT_STRING, "   %s", dn_organizational_unit);
  snprintf (menu->dialog[row++], SHORT_STRING, "   %s  %s  %s",
            dn_locality, dn_province, dn_country);
  row++;

  strfcpy (menu->dialog[row], _("This certificate was issued by:"), SHORT_STRING);
  row++;

  buflen = sizeof (dn_common_name);
  if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
                                            dn_common_name, &buflen) != 0)
    dn_common_name[0] = '\0';
  buflen = sizeof (dn_email);
  if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
                                            dn_email, &buflen) != 0)
    dn_email[0] = '\0';
  buflen = sizeof (dn_organization);
  if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0,
                                            dn_organization, &buflen) != 0)
    dn_organization[0] = '\0';
  buflen = sizeof (dn_organizational_unit);
  if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
                                            dn_organizational_unit, &buflen) != 0)
    dn_organizational_unit[0] = '\0';
  buflen = sizeof (dn_locality);
  if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0,
                                            dn_locality, &buflen) != 0)
    dn_locality[0] = '\0';
  buflen = sizeof (dn_province);
  if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0,
                                            dn_province, &buflen) != 0)
    dn_province[0] = '\0';
  buflen = sizeof (dn_country);
  if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
                                            dn_country, &buflen) != 0)
    dn_country[0] = '\0';

  snprintf (menu->dialog[row++], SHORT_STRING, "   %s  %s", dn_common_name, dn_email);
  snprintf (menu->dialog[row++], SHORT_STRING, "   %s", dn_organization);
  snprintf (menu->dialog[row++], SHORT_STRING, "   %s", dn_organizational_unit);
  snprintf (menu->dialog[row++], SHORT_STRING, "   %s  %s  %s",
            dn_locality, dn_province, dn_country);
  row++;

  snprintf (menu->dialog[row++], SHORT_STRING, _("This certificate is valid"));

  t = gnutls_x509_crt_get_activation_time (cert);
  snprintf (menu->dialog[row++], SHORT_STRING, _("   from %s"),
	    tls_make_date (t, datestr, 30));

  t = gnutls_x509_crt_get_expiration_time (cert);
  snprintf (menu->dialog[row++], SHORT_STRING, _("     to %s"),
	    tls_make_date (t, datestr, 30));

  fpbuf[0] = '\0';
  tls_fingerprint (GNUTLS_DIG_SHA, fpbuf, sizeof (fpbuf), certdata);
  snprintf (menu->dialog[row++], SHORT_STRING, _("SHA1 Fingerprint: %s"), fpbuf);
  fpbuf[0] = '\0';
  tls_fingerprint (GNUTLS_DIG_MD5, fpbuf, sizeof (fpbuf), certdata);
  snprintf (menu->dialog[row++], SHORT_STRING, _("MD5 Fingerprint: %s"), fpbuf);

  if (certerr & CERTERR_NOTYETVALID)
  {
    row++;
    strfcpy (menu->dialog[row], _("WARNING: Server certificate is not yet valid"), SHORT_STRING);
  }
  if (certerr & CERTERR_EXPIRED)
  {
    row++;
    strfcpy (menu->dialog[row], _("WARNING: Server certificate has expired"), SHORT_STRING);
  }
  if (certerr & CERTERR_REVOKED)
  {
    row++;
    strfcpy (menu->dialog[row], _("WARNING: Server certificate has been revoked"), SHORT_STRING);
  }
  if (certerr & CERTERR_HOSTNAME)
  {
    row++;
    strfcpy (menu->dialog[row], _("WARNING: Server hostname does not match certificate"), SHORT_STRING);
  }
  if (certerr & CERTERR_SIGNERNOTCA)
  {
    row++;
    strfcpy (menu->dialog[row], _("WARNING: Signer of server certificate is not a CA"), SHORT_STRING);
  }

  snprintf (title, sizeof (title),
            _("SSL Certificate check (certificate %d of %d in chain)"),
            len - idx, len);
  menu->title = title;
  /* certificates with bad dates, or that are revoked, must be
   accepted manually each and every time */
  if (SslCertFile && !savedcert
        && !(certerr & (CERTERR_EXPIRED | CERTERR_NOTYETVALID
                        | CERTERR_REVOKED)))
  {
    menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
    menu->keys = _("roa");
  }
  else
  {
    menu->prompt = _("(r)eject, accept (o)nce");
    menu->keys = _("ro");
  }

  helpstr[0] = '\0';
  mutt_make_help (buf, sizeof (buf), _("Exit  "), MENU_GENERIC, OP_EXIT);
  safe_strcat (helpstr, sizeof (helpstr), buf);
  mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
  safe_strcat (helpstr, sizeof (helpstr), buf);
  menu->help = helpstr;

  done = 0;
  set_option (OPTUNBUFFEREDINPUT);
  while (!done)
  {
    switch (mutt_menuLoop (menu))
    {
      case -1:			/* abort */
      case OP_MAX + 1:		/* reject */
      case OP_EXIT:
        done = 1;
        break;
      case OP_MAX + 3:		/* accept always */
        done = 0;
        if ((fp = fopen (SslCertFile, "a")))
	{
	  /* save hostname if necessary */
	  if (certerr & CERTERR_HOSTNAME)
	  {
	    fprintf(fp, "#H %s %s\n", hostname, fpbuf);
	    done = 1;
	  }
	  if (certerr & CERTERR_NOTTRUSTED)
	  {
            done = 0;
	    ret = gnutls_pem_base64_encode_alloc ("CERTIFICATE", certdata,
                                                  &pemdata);
	    if (ret == 0)
	    {
	      if (fwrite (pemdata.data, pemdata.size, 1, fp) == 1)
	      {
		done = 1;
	      }
              gnutls_free (pemdata.data);
	    }
	  }
	  safe_fclose (&fp);
	}
	if (!done)
        {
	  mutt_error (_("Warning: Couldn't save certificate"));
	  mutt_sleep (2);
	}
	else
        {
	  mutt_message (_("Certificate saved"));
	  mutt_sleep (0);
	}
        /* fall through */
      case OP_MAX + 2:		/* accept once */
        done = 2;
        break;
    }
  }
  unset_option (OPTUNBUFFEREDINPUT);
  mutt_menuDestroy (&menu);
  gnutls_x509_crt_deinit (cert);

  return (done == 2);
}
示例#3
0
void
pkcs11_export (FILE * outfile, const char *url, unsigned int login,
               common_info_st * info)
{
  gnutls_pkcs11_obj_t crt;
  gnutls_x509_crt_t xcrt;
  gnutls_pubkey_t pubkey;
  int ret;
  size_t size;
  unsigned int obj_flags = 0;

  if (login)
    obj_flags = GNUTLS_PKCS11_OBJ_FLAG_LOGIN;

  pkcs11_common ();

  if (url == NULL)
    url = "pkcs11:";

  ret = gnutls_pkcs11_obj_init (&crt);
  if (ret < 0)
    {
      fprintf (stderr, "Error in %s:%d: %s\n", __func__, __LINE__,
               gnutls_strerror (ret));
      exit (1);
    }

  ret = gnutls_pkcs11_obj_import_url (crt, url, obj_flags);
  if (ret < 0)
    {
      fprintf (stderr, "Error in %s:%d: %s\n", __func__, __LINE__,
               gnutls_strerror (ret));
      exit (1);
    }

  switch (gnutls_pkcs11_obj_get_type (crt))
    {
    case GNUTLS_PKCS11_OBJ_X509_CRT:
      ret = gnutls_x509_crt_init (&xcrt);
      if (ret < 0)
        {
          fprintf (stderr, "Error in %s:%d: %s\n", __func__, __LINE__,
                   gnutls_strerror (ret));
          exit (1);
        }

      ret = gnutls_x509_crt_import_pkcs11 (xcrt, crt);
      if (ret < 0)
        {
          fprintf (stderr, "Error in %s:%d: %s\n", __func__, __LINE__,
                   gnutls_strerror (ret));
          exit (1);
        }

      size = buffer_size;
      ret = gnutls_x509_crt_export (xcrt, GNUTLS_X509_FMT_PEM, buffer, &size);
      if (ret < 0)
        {
          fprintf (stderr, "Error in %s:%d: %s\n", __func__, __LINE__,
                   gnutls_strerror (ret));
          exit (1);
        }
      fwrite (buffer, 1, size, outfile);

      gnutls_x509_crt_deinit (xcrt);
      break;
    case GNUTLS_PKCS11_OBJ_PUBKEY:
      ret = gnutls_pubkey_init (&pubkey);
      if (ret < 0)
        {
          fprintf (stderr, "Error in %s:%d: %s\n", __func__, __LINE__,
                   gnutls_strerror (ret));
          exit (1);
        }

      ret = gnutls_pubkey_import_pkcs11 (pubkey, crt, 0);
      if (ret < 0)
        {
          fprintf (stderr, "Error in %s:%d: %s\n", __func__, __LINE__,
                   gnutls_strerror (ret));
          exit (1);
        }

      size = buffer_size;
      ret = gnutls_pubkey_export (pubkey, GNUTLS_X509_FMT_PEM, buffer, &size);
      if (ret < 0)
        {
          fprintf (stderr, "Error in %s:%d: %s\n", __func__, __LINE__,
                   gnutls_strerror (ret));
          exit (1);
        }
      fwrite (buffer, 1, size, outfile);

      gnutls_pubkey_deinit (pubkey);
      break;
    default:
      {
        gnutls_datum_t data, enc;

        size = buffer_size;
        ret = gnutls_pkcs11_obj_export (crt, buffer, &size);
        if (ret < 0)
          {
            break;
          }

        data.data = buffer;
        data.size = size;

        ret = gnutls_pem_base64_encode_alloc ("DATA", &data, &enc);
        if (ret < 0)
          {
            fprintf (stderr, "Error in %s:%d: %s\n", __func__, __LINE__,
                     gnutls_strerror (ret));
            exit (1);
          }

        fwrite (enc.data, 1, enc.size, outfile);

        gnutls_free (enc.data);
        break;
      }
    }
  fputs ("\n\n", outfile);


  gnutls_pkcs11_obj_deinit (crt);

  return;

}
示例#4
0
/**
 * gnutls_pkcs7_crt_print:
 * @pkcs7: The PKCS7 struct to be printed
 * @format: Indicate the format to use
 * @out: Newly allocated datum with null terminated string.
 *
 * This function will pretty print a signed PKCS #7 structure, suitable for
 * display to a human.
 *
 * Currently the supported formats are %GNUTLS_CRT_PRINT_FULL and
 * %GNUTLS_CRT_PRINT_COMPACT.
 *
 * The output @out needs to be deallocated using gnutls_free().
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 **/
int gnutls_pkcs7_print(gnutls_pkcs7_t pkcs7,
		       gnutls_certificate_print_formats_t format,
		       gnutls_datum_t * out)
{
	int count, ret, i;
	gnutls_pkcs7_signature_info_st info;
	gnutls_buffer_st str;
	const char *oid;

	_gnutls_buffer_init(&str);

	/* For backwards compatibility with structures using the default OID,
	 * we don't print the eContent Type explicitly */
	oid = gnutls_pkcs7_get_embedded_data_oid(pkcs7);
	if (oid) {
		if (strcmp(oid, DATA_OID) != 0
		    && strcmp(oid, DIGESTED_DATA_OID) != 0) {
			addf(&str, "eContent Type: %s\n", oid);
		}
	}

	for (i = 0;; i++) {
		if (i == 0)
			addf(&str, "Signers:\n");

		ret = gnutls_pkcs7_get_signature_info(pkcs7, i, &info);
		if (ret < 0)
			break;

		print_pkcs7_info(&info, &str, format);
		gnutls_pkcs7_signature_info_deinit(&info);
	}

	if (format == GNUTLS_CRT_PRINT_FULL) {
		gnutls_datum_t data, b64;

		count = gnutls_pkcs7_get_crt_count(pkcs7);

		if (count > 0) {
			addf(&str, "Number of certificates: %u\n\n",
			     count);

			for (i = 0; i < count; i++) {
				ret =
				    gnutls_pkcs7_get_crt_raw2(pkcs7, i, &data);
				if (ret < 0) {
					addf(&str,
					     "Error: cannot print certificate %d\n",
					     i);
					continue;
				}

				ret =
				    gnutls_pem_base64_encode_alloc
				    ("CERTIFICATE", &data, &b64);
				if (ret < 0) {
					gnutls_free(data.data);
					continue;
				}

				adds(&str, (char*)b64.data);
				adds(&str, "\n");
				gnutls_free(b64.data);
				gnutls_free(data.data);
			}
		}

		count = gnutls_pkcs7_get_crl_count(pkcs7);
		if (count > 0) {
			addf(&str, "Number of CRLs: %u\n\n", count);

			for (i = 0; i < count; i++) {
				ret =
				    gnutls_pkcs7_get_crl_raw2(pkcs7, i, &data);
				if (ret < 0) {
					addf(&str,
					     "Error: cannot print certificate %d\n",
					     i);
					continue;
				}

				ret =
				    gnutls_pem_base64_encode_alloc("X509 CRL",
								   &data, &b64);
				if (ret < 0) {
					gnutls_free(data.data);
					continue;
				}

				adds(&str, (char*)b64.data);
				adds(&str, "\n");
				gnutls_free(b64.data);
				gnutls_free(data.data);
			}
		}
	}

	return _gnutls_buffer_to_datum(&str, out, 1);
}
示例#5
0
/**
 * tls_check_one_certificate - Check a GnuTLS certificate
 * @param certdata List of GnuTLS certificates
 * @param certstat GnuTLS certificate status
 * @param hostname Hostname
 * @param idx      Index into certificate list
 * @param len      Length of certificate list
 * @retval 0  Failure
 * @retval >0 Success
 */
static int tls_check_one_certificate(const gnutls_datum_t *certdata,
                                     gnutls_certificate_status_t certstat,
                                     const char *hostname, int idx, size_t len)
{
  int certerr, savedcert;
  gnutls_x509_crt_t cert;
  char buf[128];
  char fpbuf[128];
  size_t buflen;
  char dn_common_name[128];
  char dn_email[128];
  char dn_organization[128];
  char dn_organizational_unit[128];
  char dn_locality[128];
  char dn_province[128];
  char dn_country[128];
  time_t t;
  char datestr[30];
  struct Menu *menu = NULL;
  char helpstr[1024];
  char title[256];
  FILE *fp = NULL;
  gnutls_datum_t pemdata;
  int row, done, ret;

  if (tls_check_preauth(certdata, certstat, hostname, idx, &certerr, &savedcert) == 0)
    return 1;

  /* interactive check from user */
  if (gnutls_x509_crt_init(&cert) < 0)
  {
    mutt_error(_("Error initialising gnutls certificate data"));
    return 0;
  }

  if (gnutls_x509_crt_import(cert, certdata, GNUTLS_X509_FMT_DER) < 0)
  {
    mutt_error(_("Error processing certificate data"));
    gnutls_x509_crt_deinit(cert);
    return 0;
  }

  menu = mutt_menu_new(MENU_GENERIC);
  menu->max = 27;
  menu->dialog = mutt_mem_calloc(1, menu->max * sizeof(char *));
  for (int i = 0; i < menu->max; i++)
    menu->dialog[i] = mutt_mem_calloc(1, dialog_row_len * sizeof(char));
  mutt_menu_push_current(menu);

  row = 0;
  mutt_str_strfcpy(menu->dialog[row], _("This certificate belongs to:"), dialog_row_len);
  row++;

  buflen = sizeof(dn_common_name);
  if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
                                    dn_common_name, &buflen) != 0)
  {
    dn_common_name[0] = '\0';
  }
  buflen = sizeof(dn_email);
  if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, &buflen) != 0)
    dn_email[0] = '\0';
  buflen = sizeof(dn_organization);
  if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0,
                                    0, dn_organization, &buflen) != 0)
  {
    dn_organization[0] = '\0';
  }
  buflen = sizeof(dn_organizational_unit);
  if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME,
                                    0, 0, dn_organizational_unit, &buflen) != 0)
  {
    dn_organizational_unit[0] = '\0';
  }
  buflen = sizeof(dn_locality);
  if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0,
                                    dn_locality, &buflen) != 0)
  {
    dn_locality[0] = '\0';
  }
  buflen = sizeof(dn_province);
  if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME,
                                    0, 0, dn_province, &buflen) != 0)
  {
    dn_province[0] = '\0';
  }
  buflen = sizeof(dn_country);
  if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
                                    dn_country, &buflen) != 0)
  {
    dn_country[0] = '\0';
  }

  snprintf(menu->dialog[row++], dialog_row_len, "   %s  %s", dn_common_name, dn_email);
  snprintf(menu->dialog[row++], dialog_row_len, "   %s", dn_organization);
  snprintf(menu->dialog[row++], dialog_row_len, "   %s", dn_organizational_unit);
  snprintf(menu->dialog[row++], dialog_row_len, "   %s  %s  %s", dn_locality,
           dn_province, dn_country);
  row++;

  mutt_str_strfcpy(menu->dialog[row], _("This certificate was issued by:"), dialog_row_len);
  row++;

  buflen = sizeof(dn_common_name);
  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0,
                                           0, dn_common_name, &buflen) != 0)
  {
    dn_common_name[0] = '\0';
  }
  buflen = sizeof(dn_email);
  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
                                           dn_email, &buflen) != 0)
  {
    dn_email[0] = '\0';
  }
  buflen = sizeof(dn_organization);
  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATION_NAME,
                                           0, 0, dn_organization, &buflen) != 0)
  {
    dn_organization[0] = '\0';
  }
  buflen = sizeof(dn_organizational_unit);
  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME,
                                           0, 0, dn_organizational_unit, &buflen) != 0)
  {
    dn_organizational_unit[0] = '\0';
  }
  buflen = sizeof(dn_locality);
  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_LOCALITY_NAME,
                                           0, 0, dn_locality, &buflen) != 0)
  {
    dn_locality[0] = '\0';
  }
  buflen = sizeof(dn_province);
  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME,
                                           0, 0, dn_province, &buflen) != 0)
  {
    dn_province[0] = '\0';
  }
  buflen = sizeof(dn_country);
  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_COUNTRY_NAME,
                                           0, 0, dn_country, &buflen) != 0)
  {
    dn_country[0] = '\0';
  }

  snprintf(menu->dialog[row++], dialog_row_len, "   %s  %s", dn_common_name, dn_email);
  snprintf(menu->dialog[row++], dialog_row_len, "   %s", dn_organization);
  snprintf(menu->dialog[row++], dialog_row_len, "   %s", dn_organizational_unit);
  snprintf(menu->dialog[row++], dialog_row_len, "   %s  %s  %s", dn_locality,
           dn_province, dn_country);
  row++;

  snprintf(menu->dialog[row++], dialog_row_len, _("This certificate is valid"));

  t = gnutls_x509_crt_get_activation_time(cert);
  mutt_date_make_tls(datestr, sizeof(datestr), t);
  snprintf(menu->dialog[row++], dialog_row_len, _("   from %s"), datestr);

  t = gnutls_x509_crt_get_expiration_time(cert);
  mutt_date_make_tls(datestr, sizeof(datestr), t);
  snprintf(menu->dialog[row++], dialog_row_len, _("     to %s"), datestr);

  fpbuf[0] = '\0';
  tls_fingerprint(GNUTLS_DIG_SHA, fpbuf, sizeof(fpbuf), certdata);
  snprintf(menu->dialog[row++], dialog_row_len, _("SHA1 Fingerprint: %s"), fpbuf);
  fpbuf[0] = '\0';
  fpbuf[40] = '\0'; /* Ensure the second printed line is null terminated */
  tls_fingerprint(GNUTLS_DIG_SHA256, fpbuf, sizeof(fpbuf), certdata);
  fpbuf[39] = '\0'; /* Divide into two lines of output */
  snprintf(menu->dialog[row++], dialog_row_len, "%s%s", _("SHA256 Fingerprint: "), fpbuf);
  snprintf(menu->dialog[row++], dialog_row_len, "%*s%s",
           (int) mutt_str_strlen(_("SHA256 Fingerprint: ")), "", fpbuf + 40);

  if (certerr & CERTERR_NOTYETVALID)
  {
    row++;
    mutt_str_strfcpy(menu->dialog[row],
                     _("WARNING: Server certificate is not yet valid"), dialog_row_len);
  }
  if (certerr & CERTERR_EXPIRED)
  {
    row++;
    mutt_str_strfcpy(menu->dialog[row],
                     _("WARNING: Server certificate has expired"), dialog_row_len);
  }
  if (certerr & CERTERR_REVOKED)
  {
    row++;
    mutt_str_strfcpy(menu->dialog[row],
                     _("WARNING: Server certificate has been revoked"), dialog_row_len);
  }
  if (certerr & CERTERR_HOSTNAME)
  {
    row++;
    mutt_str_strfcpy(menu->dialog[row],
                     _("WARNING: Server hostname does not match certificate"),
                     dialog_row_len);
  }
  if (certerr & CERTERR_SIGNERNOTCA)
  {
    row++;
    mutt_str_strfcpy(menu->dialog[row],
                     _("WARNING: Signer of server certificate is not a CA"), dialog_row_len);
  }

  snprintf(title, sizeof(title),
           _("SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len);
  menu->title = title;
  /* certificates with bad dates, or that are revoked, must be
   * accepted manually each and every time */
  if (C_CertificateFile && !savedcert &&
      !(certerr & (CERTERR_EXPIRED | CERTERR_NOTYETVALID | CERTERR_REVOKED)))
  {
    menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
    /* L10N: These three letters correspond to the choices in the string:
       (r)eject, accept (o)nce, (a)ccept always.
       This is an interactive certificate confirmation prompt for
       a GNUTLS connection. */
    menu->keys = _("roa");
  }
  else
  {
    menu->prompt = _("(r)eject, accept (o)nce");
    /* L10N: These two letters correspond to the choices in the string:
       (r)eject, accept (o)nce.
       These is an interactive certificate confirmation prompt for
       a GNUTLS connection. */
    menu->keys = _("ro");
  }

  helpstr[0] = '\0';
  mutt_make_help(buf, sizeof(buf), _("Exit  "), MENU_GENERIC, OP_EXIT);
  mutt_str_strcat(helpstr, sizeof(helpstr), buf);
  mutt_make_help(buf, sizeof(buf), _("Help"), MENU_GENERIC, OP_HELP);
  mutt_str_strcat(helpstr, sizeof(helpstr), buf);
  menu->help = helpstr;

  done = 0;
  OptIgnoreMacroEvents = true;
  while (done == 0)
  {
    switch (mutt_menu_loop(menu))
    {
      case -1:         /* abort */
      case OP_MAX + 1: /* reject */
      case OP_EXIT:
        done = 1;
        break;
      case OP_MAX + 3: /* accept always */
        done = 0;
        fp = mutt_file_fopen(C_CertificateFile, "a");
        if (fp)
        {
          /* save hostname if necessary */
          if (certerr & CERTERR_HOSTNAME)
          {
            fpbuf[0] = '\0';
            tls_fingerprint(GNUTLS_DIG_MD5, fpbuf, sizeof(fpbuf), certdata);
            fprintf(fp, "#H %s %s\n", hostname, fpbuf);
            done = 1;
          }
          /* Save the cert for all other errors */
          if (certerr ^ CERTERR_HOSTNAME)
          {
            done = 0;
            ret = gnutls_pem_base64_encode_alloc("CERTIFICATE", certdata, &pemdata);
            if (ret == 0)
            {
              if (fwrite(pemdata.data, pemdata.size, 1, fp) == 1)
              {
                done = 1;
              }
              gnutls_free(pemdata.data);
            }
          }
          mutt_file_fclose(&fp);
        }
        if (done == 0)
        {
          mutt_error(_("Warning: Couldn't save certificate"));
        }
        else
        {
          mutt_message(_("Certificate saved"));
          mutt_sleep(0);
        }
      /* fallthrough */
      case OP_MAX + 2: /* accept once */
        done = 2;
        break;
    }
  }
  OptIgnoreMacroEvents = false;
  mutt_menu_pop_current(menu);
  mutt_menu_destroy(&menu);
  gnutls_x509_crt_deinit(cert);

  return done == 2;
}