aboutsummaryrefslogtreecommitdiffstats
path: root/common/rfb/CSecurityTLS.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'common/rfb/CSecurityTLS.cxx')
-rw-r--r--common/rfb/CSecurityTLS.cxx450
1 files changed, 240 insertions, 210 deletions
diff --git a/common/rfb/CSecurityTLS.cxx b/common/rfb/CSecurityTLS.cxx
index 0c10a85d..6eefe73b 100644
--- a/common/rfb/CSecurityTLS.cxx
+++ b/common/rfb/CSecurityTLS.cxx
@@ -3,7 +3,7 @@
* Copyright (C) 2005 Martin Koegler
* Copyright (C) 2010 TigerVNC Team
* Copyright (C) 2010 m-privacy GmbH
- * Copyright (C) 2012-2021 Pierre Ossman for Cendio AB
+ * Copyright 2012-2025 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -34,15 +34,16 @@
#include <unistd.h>
#endif
+#include <core/LogWriter.h>
+#include <core/string.h>
+#include <core/xdgdirs.h>
+
#include <rfb/CSecurityTLS.h>
#include <rfb/CConnection.h>
-#include <rfb/LogWriter.h>
#include <rfb/Exception.h>
-#include <rfb/util.h>
+
#include <rdr/TLSException.h>
-#include <rdr/TLSInStream.h>
-#include <rdr/TLSOutStream.h>
-#include <os/os.h>
+#include <rdr/TLSSocket.h>
#include <gnutls/x509.h>
@@ -50,21 +51,19 @@ using namespace rfb;
static const char* configdirfn(const char* fn);
-StringParameter CSecurityTLS::X509CA("X509CA", "X509 CA certificate",
- configdirfn("x509_ca.pem"),
- ConfViewer);
-StringParameter CSecurityTLS::X509CRL("X509CRL", "X509 CRL file",
- configdirfn("x509_crl.pem"),
- ConfViewer);
+core::StringParameter CSecurityTLS::X509CA("X509CA", "X509 CA certificate",
+ configdirfn("x509_ca.pem"));
+core::StringParameter CSecurityTLS::X509CRL("X509CRL", "X509 CRL file",
+ configdirfn("x509_crl.pem"));
-static LogWriter vlog("TLS");
+static core::LogWriter vlog("TLS");
static const char* configdirfn(const char* fn)
{
static char full_path[PATH_MAX];
const char* configdir;
- configdir = os::getvncconfigdir();
+ configdir = core::getvncconfigdir();
if (configdir == nullptr)
return "";
@@ -75,7 +74,7 @@ static const char* configdirfn(const char* fn)
CSecurityTLS::CSecurityTLS(CConnection* cc_, bool _anon)
: CSecurity(cc_), session(nullptr),
anon_cred(nullptr), cert_cred(nullptr),
- anon(_anon), tlsis(nullptr), tlsos(nullptr),
+ anon(_anon), tlssock(nullptr),
rawis(nullptr), rawos(nullptr)
{
int err = gnutls_global_init();
@@ -85,27 +84,8 @@ CSecurityTLS::CSecurityTLS(CConnection* cc_, bool _anon)
void CSecurityTLS::shutdown()
{
- if (tlsos) {
- try {
- if (tlsos->hasBufferedData()) {
- tlsos->cork(false);
- tlsos->flush();
- if (tlsos->hasBufferedData())
- vlog.error("Failed to flush remaining socket data on close");
- }
- } catch (std::exception& e) {
- vlog.error("Failed to flush remaining socket data on close: %s", e.what());
- }
- }
-
- if (session) {
- int ret;
- // FIXME: We can't currently wait for the response, so we only send
- // our close and hope for the best
- ret = gnutls_bye(session, GNUTLS_SHUT_WR);
- if ((ret != GNUTLS_E_SUCCESS) && (ret != GNUTLS_E_INVALID_SESSION))
- vlog.error("TLS shutdown failed: %s", gnutls_strerror(ret));
- }
+ if (tlssock)
+ tlssock->shutdown();
if (anon_cred) {
gnutls_anon_free_client_credentials(anon_cred);
@@ -123,13 +103,9 @@ void CSecurityTLS::shutdown()
rawos = nullptr;
}
- if (tlsis) {
- delete tlsis;
- tlsis = nullptr;
- }
- if (tlsos) {
- delete tlsos;
- tlsos = nullptr;
+ if (tlssock) {
+ delete tlssock;
+ tlssock = nullptr;
}
if (session) {
@@ -171,26 +147,18 @@ bool CSecurityTLS::processMsg()
setParam();
- // Create these early as they set up the push/pull functions
- // for GnuTLS
- tlsis = new rdr::TLSInStream(is, session);
- tlsos = new rdr::TLSOutStream(os, session);
+ tlssock = new rdr::TLSSocket(is, os, session);
rawis = is;
rawos = os;
}
- int err;
- err = gnutls_handshake(session);
- if (err != GNUTLS_E_SUCCESS) {
- if (!gnutls_error_is_fatal(err)) {
- vlog.debug("Deferring completion of TLS handshake: %s", gnutls_strerror(err));
+ try {
+ if (!tlssock->handshake())
return false;
- }
-
- vlog.error("TLS Handshake failed: %s\n", gnutls_strerror (err));
+ } catch (std::exception&) {
shutdown();
- throw rdr::tls_error("TLS Handshake failed", err);
+ throw;
}
vlog.debug("TLS handshake completed with %s",
@@ -198,33 +166,29 @@ bool CSecurityTLS::processMsg()
checkSession();
- cc->setStreams(tlsis, tlsos);
+ cc->setStreams(&tlssock->inStream(), &tlssock->outStream());
return true;
}
void CSecurityTLS::setParam()
{
- static const char kx_anon_priority[] = ":+ANON-ECDH:+ANON-DH";
+ static const char kx_anon_priority[] = "+ANON-ECDH:+ANON-DH";
int ret;
// Custom priority string specified?
if (strcmp(Security::GnuTLSPriority, "") != 0) {
- char *prio;
+ std::string prio;
const char *err;
- prio = new char[strlen(Security::GnuTLSPriority) +
- strlen(kx_anon_priority) + 1];
-
- strcpy(prio, Security::GnuTLSPriority);
- if (anon)
- strcat(prio, kx_anon_priority);
-
- ret = gnutls_priority_set_direct(session, prio, &err);
-
- delete [] prio;
+ prio = (const char*)Security::GnuTLSPriority;
+ if (anon) {
+ prio += ":";
+ prio += kx_anon_priority;
+ }
+ ret = gnutls_priority_set_direct(session, prio.c_str(), &err);
if (ret != GNUTLS_E_SUCCESS) {
if (ret == GNUTLS_E_INVALID_REQUEST)
vlog.error("GnuTLS priority syntax error at: %s", err);
@@ -234,30 +198,22 @@ void CSecurityTLS::setParam()
const char *err;
#if GNUTLS_VERSION_NUMBER >= 0x030603
- // gnutls_set_default_priority_appends() expects a normal priority string that
- // doesn't start with ":".
- ret = gnutls_set_default_priority_append(session, kx_anon_priority + 1, &err, 0);
+ ret = gnutls_set_default_priority_append(session, kx_anon_priority, &err, 0);
if (ret != GNUTLS_E_SUCCESS) {
if (ret == GNUTLS_E_INVALID_REQUEST)
vlog.error("GnuTLS priority syntax error at: %s", err);
throw rdr::tls_error("gnutls_set_default_priority_append()", ret);
}
#else
+ std::string prio;
+
// We don't know what the system default priority is, so we guess
// it's what upstream GnuTLS has
- static const char gnutls_default_priority[] = "NORMAL";
- char *prio;
-
- prio = new char[malloc(strlen(gnutls_default_priority) +
- strlen(kx_anon_priority) + 1];
-
- strcpy(prio, gnutls_default_priority);
- strcat(prio, kx_anon_priority);
-
- ret = gnutls_priority_set_direct(session, prio, &err);
-
- delete [] prio;
+ prio = "NORMAL";
+ prio += ":";
+ prio += kx_anon_priority;
+ ret = gnutls_priority_set_direct(session, prio.c_str(), &err);
if (ret != GNUTLS_E_SUCCESS) {
if (ret == GNUTLS_E_INVALID_REQUEST)
vlog.error("GnuTLS priority syntax error at: %s", err);
@@ -277,6 +233,10 @@ void CSecurityTLS::setParam()
vlog.debug("Anonymous session has been set");
} else {
+ const char* hostname;
+ size_t len;
+ bool valid;
+
ret = gnutls_certificate_allocate_credentials(&cert_cred);
if (ret != GNUTLS_E_SUCCESS)
throw rdr::tls_error("gnutls_certificate_allocate_credentials()", ret);
@@ -294,10 +254,22 @@ void CSecurityTLS::setParam()
if (ret != GNUTLS_E_SUCCESS)
throw rdr::tls_error("gnutls_credentials_set()", ret);
- if (gnutls_server_name_set(session, GNUTLS_NAME_DNS,
- client->getServerName(),
- strlen(client->getServerName())) != GNUTLS_E_SUCCESS)
- vlog.error("Failed to configure the server name for TLS handshake");
+ // Only DNS hostnames are allowed, and some servers will reject the
+ // connection if we provide anything else (e.g. an IPv6 address)
+ hostname = client->getServerName();
+ len = strlen(hostname);
+ valid = true;
+ for (size_t i = 0; i < len; i++) {
+ if (!isalnum(hostname[i]) && hostname[i] != '.')
+ valid = false;
+ }
+
+ if (valid) {
+ if (gnutls_server_name_set(session, GNUTLS_NAME_DNS,
+ client->getServerName(),
+ strlen(client->getServerName())) != GNUTLS_E_SUCCESS)
+ vlog.error("Failed to configure the server name for TLS handshake");
+ }
vlog.debug("X509 session has been set");
}
@@ -324,12 +296,16 @@ void CSecurityTLS::checkSession()
if (anon)
return;
- if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509)
+ if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_UNSUPPORTED_CERTIFICATE);
throw protocol_error("Unsupported certificate type");
+ }
err = gnutls_certificate_verify_peers2(session, &status);
if (err != 0) {
vlog.error("Server certificate verification failed: %s", gnutls_strerror(err));
+ gnutls_alert_send_appropriate(session, err);
throw rdr::tls_error("Server certificate verification()", err);
}
@@ -346,23 +322,29 @@ void CSecurityTLS::checkSession()
GNUTLS_CRT_X509,
&status_str,
0);
- if (err != GNUTLS_E_SUCCESS)
+ if (err != GNUTLS_E_SUCCESS) {
+ gnutls_alert_send_appropriate(session, err);
throw rdr::tls_error("Failed to get certificate error description", err);
+ }
error = (const char*)status_str.data;
gnutls_free(status_str.data);
- throw protocol_error(format("Invalid server certificate: %s",
- error.c_str()));
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
+ throw protocol_error(
+ core::format("Invalid server certificate: %s", error.c_str()));
}
err = gnutls_certificate_verification_status_print(status,
GNUTLS_CRT_X509,
&status_str,
0);
- if (err != GNUTLS_E_SUCCESS)
+ if (err != GNUTLS_E_SUCCESS) {
+ gnutls_alert_send_appropriate(session, err);
throw rdr::tls_error("Failed to get certificate error description", err);
+ }
vlog.info("Server certificate errors: %s", status_str.data);
@@ -372,16 +354,21 @@ void CSecurityTLS::checkSession()
/* Process overridable errors later */
cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
- if (!cert_list_size)
+ if (!cert_list_size) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_UNSUPPORTED_CERTIFICATE);
throw protocol_error("Empty certificate chain");
+ }
/* Process only server's certificate, not issuer's certificate */
gnutls_x509_crt_t crt;
gnutls_x509_crt_init(&crt);
err = gnutls_x509_crt_import(crt, &cert_list[0], GNUTLS_X509_FMT_DER);
- if (err != GNUTLS_E_SUCCESS)
+ if (err != GNUTLS_E_SUCCESS) {
+ gnutls_alert_send_appropriate(session, err);
throw rdr::tls_error("Failed to decode server certificate", err);
+ }
if (gnutls_x509_crt_check_hostname(crt, client->getServerName()) == 0) {
vlog.info("Server certificate doesn't match given server name");
@@ -398,7 +385,7 @@ void CSecurityTLS::checkSession()
/* Certificate has some user overridable problems, so TOFU time */
- hostsDir = os::getvncstatedir();
+ hostsDir = core::getvncstatedir();
if (hostsDir == nullptr) {
throw std::runtime_error("Could not obtain VNC state directory "
"path for known hosts storage");
@@ -420,12 +407,15 @@ void CSecurityTLS::checkSession()
if ((known != GNUTLS_E_NO_CERTIFICATE_FOUND) &&
(known != GNUTLS_E_CERTIFICATE_KEY_MISMATCH)) {
+ gnutls_alert_send_appropriate(session, known);
throw rdr::tls_error("Could not load known hosts database", known);
}
err = gnutls_x509_crt_print(crt, GNUTLS_CRT_PRINT_ONELINE, &info);
- if (err != GNUTLS_E_SUCCESS)
+ if (err != GNUTLS_E_SUCCESS) {
+ gnutls_alert_send_appropriate(session, known);
throw rdr::tls_error("Could not find certificate to display", err);
+ }
len = strlen((char*)info.data);
for (size_t i = 0; i < len - 1; i++) {
@@ -443,21 +433,24 @@ void CSecurityTLS::checkSession()
if (status & (GNUTLS_CERT_INVALID |
GNUTLS_CERT_SIGNER_NOT_FOUND |
GNUTLS_CERT_SIGNER_NOT_CA)) {
- text = format("This certificate has been signed by an unknown "
- "authority:\n"
- "\n"
- "%s\n"
- "\n"
- "Someone could be trying to impersonate the site "
- "and you should not continue.\n"
- "\n"
- "Do you want to make an exception for this "
- "server?", info.data);
+ text = core::format(
+ "This certificate has been signed by an unknown authority:\n"
+ "\n"
+ "%s\n"
+ "\n"
+ "Someone could be trying to impersonate the site and you "
+ "should not continue.\n"
+ "\n"
+ "Do you want to make an exception for this server?",
+ info.data);
if (!cc->showMsgBox(MsgBoxFlags::M_YESNO,
"Unknown certificate issuer",
- text.c_str()))
+ text.c_str())) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_UNKNOWN_CA);
throw auth_cancelled();
+ }
status &= ~(GNUTLS_CERT_INVALID |
GNUTLS_CERT_SIGNER_NOT_FOUND |
@@ -465,82 +458,101 @@ void CSecurityTLS::checkSession()
}
if (status & GNUTLS_CERT_NOT_ACTIVATED) {
- text = format("This certificate is not yet valid:\n"
- "\n"
- "%s\n"
- "\n"
- "Someone could be trying to impersonate the site "
- "and you should not continue.\n"
- "\n"
- "Do you want to make an exception for this "
- "server?", info.data);
+ text = core::format(
+ "This certificate is not yet valid:\n"
+ "\n"
+ "%s\n"
+ "\n"
+ "Someone could be trying to impersonate the site and you "
+ "should not continue.\n"
+ "\n"
+ "Do you want to make an exception for this server?",
+ info.data);
+
if (!cc->showMsgBox(MsgBoxFlags::M_YESNO,
"Certificate is not yet valid",
- text.c_str()))
+ text.c_str())) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw auth_cancelled();
+ }
status &= ~GNUTLS_CERT_NOT_ACTIVATED;
}
if (status & GNUTLS_CERT_EXPIRED) {
- text = format("This certificate has expired:\n"
- "\n"
- "%s\n"
- "\n"
- "Someone could be trying to impersonate the site "
- "and you should not continue.\n"
- "\n"
- "Do you want to make an exception for this "
- "server?", info.data);
+ text = core::format(
+ "This certificate has expired:\n"
+ "\n"
+ "%s\n"
+ "\n"
+ "Someone could be trying to impersonate the site and you "
+ "should not continue.\n"
+ "\n"
+ "Do you want to make an exception for this server?",
+ info.data);
if (!cc->showMsgBox(MsgBoxFlags::M_YESNO,
"Expired certificate",
- text.c_str()))
+ text.c_str())) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw auth_cancelled();
+ }
status &= ~GNUTLS_CERT_EXPIRED;
}
if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
- text = format("This certificate uses an insecure algorithm:\n"
- "\n"
- "%s\n"
- "\n"
- "Someone could be trying to impersonate the site "
- "and you should not continue.\n"
- "\n"
- "Do you want to make an exception for this "
- "server?", info.data);
+ text = core::format(
+ "This certificate uses an insecure algorithm:\n"
+ "\n"
+ "%s\n"
+ "\n"
+ "Someone could be trying to impersonate the site and you "
+ "should not continue.\n"
+ "\n"
+ "Do you want to make an exception for this server?",
+ info.data);
if (!cc->showMsgBox(MsgBoxFlags::M_YESNO,
"Insecure certificate algorithm",
- text.c_str()))
+ text.c_str())) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw auth_cancelled();
+ }
status &= ~GNUTLS_CERT_INSECURE_ALGORITHM;
}
if (status != 0) {
vlog.error("Unhandled certificate problems: 0x%x", status);
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw std::logic_error("Unhandled certificate problems");
}
if (!hostname_match) {
- text = format("The specified hostname \"%s\" does not match the "
- "certificate provided by the server:\n"
- "\n"
- "%s\n"
- "\n"
- "Someone could be trying to impersonate the site "
- "and you should not continue.\n"
- "\n"
- "Do you want to make an exception for this "
- "server?", client->getServerName(), info.data);
+ text = core::format(
+ "The specified hostname \"%s\" does not match the certificate "
+ "provided by the server:\n"
+ "\n"
+ "%s\n"
+ "\n"
+ "Someone could be trying to impersonate the site and you "
+ "should not continue.\n"
+ "\n"
+ "Do you want to make an exception for this server?",
+ client->getServerName(), info.data);
if (!cc->showMsgBox(MsgBoxFlags::M_YESNO,
"Certificate hostname mismatch",
- text.c_str()))
+ text.c_str())) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw auth_cancelled();
+ }
}
} else if (known == GNUTLS_E_CERTIFICATE_KEY_MISMATCH) {
std::string text;
@@ -551,22 +563,26 @@ void CSecurityTLS::checkSession()
if (status & (GNUTLS_CERT_INVALID |
GNUTLS_CERT_SIGNER_NOT_FOUND |
GNUTLS_CERT_SIGNER_NOT_CA)) {
- text = format("This host is previously known with a different "
- "certificate, and the new certificate has been "
- "signed by an unknown authority:\n"
- "\n"
- "%s\n"
- "\n"
- "Someone could be trying to impersonate the site "
- "and you should not continue.\n"
- "\n"
- "Do you want to make an exception for this "
- "server?", info.data);
+ text = core::format(
+ "This host is previously known with a different certificate, "
+ "and the new certificate has been signed by an unknown "
+ "authority:\n"
+ "\n"
+ "%s\n"
+ "\n"
+ "Someone could be trying to impersonate the site and you "
+ "should not continue.\n"
+ "\n"
+ "Do you want to make an exception for this server?",
+ info.data);
if (!cc->showMsgBox(MsgBoxFlags::M_YESNO,
"Unexpected server certificate",
- text.c_str()))
+ text.c_str())) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_UNKNOWN_CA);
throw auth_cancelled();
+ }
status &= ~(GNUTLS_CERT_INVALID |
GNUTLS_CERT_SIGNER_NOT_FOUND |
@@ -574,91 +590,105 @@ void CSecurityTLS::checkSession()
}
if (status & GNUTLS_CERT_NOT_ACTIVATED) {
- text = format("This host is previously known with a different "
- "certificate, and the new certificate is not yet "
- "valid:\n"
- "\n"
- "%s\n"
- "\n"
- "Someone could be trying to impersonate the site "
- "and you should not continue.\n"
- "\n"
- "Do you want to make an exception for this "
- "server?", info.data);
+ text = core::format(
+ "This host is previously known with a different certificate, "
+ "and the new certificate is not yet valid:\n"
+ "\n"
+ "%s\n"
+ "\n"
+ "Someone could be trying to impersonate the site and you "
+ "should not continue.\n"
+ "\n"
+ "Do you want to make an exception for this server?",
+ info.data);
if (!cc->showMsgBox(MsgBoxFlags::M_YESNO,
"Unexpected server certificate",
- text.c_str()))
+ text.c_str())) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw auth_cancelled();
+ }
status &= ~GNUTLS_CERT_NOT_ACTIVATED;
}
if (status & GNUTLS_CERT_EXPIRED) {
- text = format("This host is previously known with a different "
- "certificate, and the new certificate has "
- "expired:\n"
- "\n"
- "%s\n"
- "\n"
- "Someone could be trying to impersonate the site "
- "and you should not continue.\n"
- "\n"
- "Do you want to make an exception for this "
- "server?", info.data);
+ text = core::format(
+ "This host is previously known with a different certificate, "
+ "and the new certificate has expired:\n"
+ "\n"
+ "%s\n"
+ "\n"
+ "Someone could be trying to impersonate the site and you "
+ "should not continue.\n"
+ "\n"
+ "Do you want to make an exception for this server?",
+ info.data);
if (!cc->showMsgBox(MsgBoxFlags::M_YESNO,
"Unexpected server certificate",
- text.c_str()))
+ text.c_str())) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw auth_cancelled();
+ }
status &= ~GNUTLS_CERT_EXPIRED;
}
if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
- text = format("This host is previously known with a different "
- "certificate, and the new certificate uses an "
- "insecure algorithm:\n"
- "\n"
- "%s\n"
- "\n"
- "Someone could be trying to impersonate the site "
- "and you should not continue.\n"
- "\n"
- "Do you want to make an exception for this "
- "server?", info.data);
+ text = core::format(
+ "This host is previously known with a different certificate, "
+ "and the new certificate uses an insecure algorithm:\n"
+ "\n"
+ "%s\n"
+ "\n"
+ "Someone could be trying to impersonate the site and you "
+ "should not continue.\n"
+ "\n"
+ "Do you want to make an exception for this server?",
+ info.data);
if (!cc->showMsgBox(MsgBoxFlags::M_YESNO,
"Unexpected server certificate",
- text.c_str()))
+ text.c_str())) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw auth_cancelled();
+ }
status &= ~GNUTLS_CERT_INSECURE_ALGORITHM;
}
if (status != 0) {
vlog.error("Unhandled certificate problems: 0x%x", status);
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw std::logic_error("Unhandled certificate problems");
}
if (!hostname_match) {
- text = format("This host is previously known with a different "
- "certificate, and the specified hostname \"%s\" "
- "does not match the new certificate provided by "
- "the server:\n"
- "\n"
- "%s\n"
- "\n"
- "Someone could be trying to impersonate the site "
- "and you should not continue.\n"
- "\n"
- "Do you want to make an exception for this "
- "server?", client->getServerName(), info.data);
+ text = core::format(
+ "This host is previously known with a different certificate, "
+ "and the specified hostname \"%s\" does not match the new "
+ "certificate provided by the server:\n"
+ "\n"
+ "%s\n"
+ "\n"
+ "Someone could be trying to impersonate the site and you "
+ "should not continue.\n"
+ "\n"
+ "Do you want to make an exception for this server?",
+ client->getServerName(), info.data);
if (!cc->showMsgBox(MsgBoxFlags::M_YESNO,
"Unexpected server certificate",
- text.c_str()))
+ text.c_str())) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw auth_cancelled();
+ }
}
}