Bladeren bron

Properly store certificate exceptions

The previous method stored the certificates as authorities, meaning that
the owner of that certificate could impersonate any server it wanted
after a client had added an exception.

Handle this more properly by only storing exceptions for specific
hostname/certificate combinations, the same way browsers or SSH does
things.
tags/v1.11.90
Pierre Ossman 4 jaren geleden
bovenliggende
commit
b30f10c681
1 gewijzigde bestanden met toevoegingen van 73 en 90 verwijderingen
  1. 73
    90
      common/rfb/CSecurityTLS.cxx

+ 73
- 90
common/rfb/CSecurityTLS.cxx Bestand weergeven

if (*cafile && gnutls_certificate_set_x509_trust_file(cert_cred,cafile,GNUTLS_X509_FMT_PEM) < 0) if (*cafile && gnutls_certificate_set_x509_trust_file(cert_cred,cafile,GNUTLS_X509_FMT_PEM) < 0)
throw AuthFailureException("load of CA cert failed"); throw AuthFailureException("load of CA cert failed");


/* Load previously saved certs */
char *homeDir = NULL;
int err;
if (getvnchomedir(&homeDir) == -1)
vlog.error("Could not obtain VNC home directory path");
else {
CharArray caSave(strlen(homeDir) + 19 + 1);
sprintf(caSave.buf, "%sx509_savedcerts.pem", homeDir);
delete [] homeDir;

err = gnutls_certificate_set_x509_trust_file(cert_cred, caSave.buf,
GNUTLS_X509_FMT_PEM);
if (err < 0)
vlog.debug("Failed to load saved server certificates from %s", caSave.buf);
}

if (*crlfile && gnutls_certificate_set_x509_crl_file(cert_cred,crlfile,GNUTLS_X509_FMT_PEM) < 0) if (*crlfile && gnutls_certificate_set_x509_crl_file(cert_cred,crlfile,GNUTLS_X509_FMT_PEM) < 0)
throw AuthFailureException("load of CRL failed"); throw AuthFailureException("load of CRL failed");


const gnutls_datum_t *cert_list; const gnutls_datum_t *cert_list;
unsigned int cert_list_size = 0; unsigned int cert_list_size = 0;
int err; int err;

char *homeDir;
gnutls_datum_t info; gnutls_datum_t info;
size_t len;


if (anon) if (anon)
return; return;
throw AuthFailureException("decoding of certificate failed"); throw AuthFailureException("decoding of certificate failed");


if (gnutls_x509_crt_check_hostname(crt, client->getServerName()) == 0) { if (gnutls_x509_crt_check_hostname(crt, client->getServerName()) == 0) {
char buf[255];
CharArray text;
vlog.debug("hostname mismatch"); vlog.debug("hostname mismatch");
snprintf(buf, sizeof(buf), "Hostname (%s) does not match any certificate, "
"do you want to continue?", client->getServerName());
buf[sizeof(buf) - 1] = '\0';
if (!msg->showMsgBox(UserMsgBox::M_YESNO, "hostname mismatch", buf))
throw AuthFailureException("hostname mismatch");
text.format("Hostname (%s) does not match the server certificate, "
"do you want to continue?", client->getServerName());
if (!msg->showMsgBox(UserMsgBox::M_YESNO,
"Certificate hostname mismatch", text.buf))
throw AuthFailureException("Certificate hostname mismatch");
} }


if (status == 0) { if (status == 0) {
throw AuthFailureException("Invalid status of server certificate verification"); throw AuthFailureException("Invalid status of server certificate verification");
} }


vlog.debug("Saved server certificates don't match");
/* Certificate is fine, except we don't know the issuer, so TOFU time */


if (gnutls_x509_crt_print(crt, GNUTLS_CRT_PRINT_ONELINE, &info)) {
/*
* GNUTLS doesn't correctly export gnutls_free symbol which is
* a function pointer. Linking with Visual Studio 2008 Express will
* fail when you call gnutls_free().
*/
#if WIN32
free(info.data);
#else
gnutls_free(info.data);
#endif
throw AuthFailureException("Could not find certificate to display");
homeDir = NULL;
if (getvnchomedir(&homeDir) == -1) {
throw AuthFailureException("Could not obtain VNC home directory "
"path for known hosts storage");
} }


size_t out_size = 0;
char *out_buf = NULL;
char *certinfo = NULL;
int len = 0;

vlog.debug("certificate issuer unknown");

len = snprintf(NULL, 0, "This certificate has been signed by an unknown "
"authority:\n\n%s\n\nDo you want to save it and "
"continue?\n ", info.data);
if (len < 0)
throw AuthFailureException("certificate decoding error");

vlog.debug("%s", info.data);

certinfo = new char[len];

snprintf(certinfo, len, "This certificate has been signed by an unknown "
"authority:\n\n%s\n\nDo you want to save it and "
"continue? ", info.data);
CharArray dbPath(strlen(homeDir) + 16 + 1);
sprintf(dbPath.buf, "%sx509_known_hosts", homeDir);
delete [] homeDir;


for (int i = 0; i < len - 1; i++)
if (certinfo[i] == ',' && certinfo[i + 1] == ' ')
certinfo[i] = '\n';
err = gnutls_verify_stored_pubkey(dbPath.buf, NULL,
client->getServerName(), NULL,
GNUTLS_CRT_X509, &cert_list[0], 0);


if (!msg->showMsgBox(UserMsgBox::M_YESNO, "certificate issuer unknown",
certinfo)) {
delete [] certinfo;
throw AuthFailureException("certificate issuer unknown");
/* Previously known? */
if (err == GNUTLS_E_SUCCESS) {
vlog.debug("Server certificate found in known hosts file");
gnutls_x509_crt_deinit(crt);
return;
} }


delete [] certinfo;

if (gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, NULL, &out_size)
!= GNUTLS_E_SHORT_MEMORY_BUFFER)
throw AuthFailureException("certificate issuer unknown, and certificate "
"export failed");
if ((err != GNUTLS_E_NO_CERTIFICATE_FOUND) &&
(err != GNUTLS_E_CERTIFICATE_KEY_MISMATCH)) {
throw AuthFailureException("Could not load known hosts database");
}


// Save cert
out_buf = new char[out_size];
if (gnutls_x509_crt_print(crt, GNUTLS_CRT_PRINT_ONELINE, &info))
throw AuthFailureException("Could not find certificate to display");


if (gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, out_buf, &out_size) < 0)
throw AuthFailureException("certificate issuer unknown, and certificate "
"export failed");
len = strlen((char*)info.data);
for (size_t i = 0; i < len - 1; i++) {
if (info.data[i] == ',' && info.data[i + 1] == ' ')
info.data[i] = '\n';
}


char *homeDir = NULL;
if (getvnchomedir(&homeDir) == -1)
vlog.error("Could not obtain VNC home directory path");
else {
FILE *f;
CharArray caSave(strlen(homeDir) + 1 + 19);
sprintf(caSave.buf, "%sx509_savedcerts.pem", homeDir);
delete [] homeDir;
f = fopen(caSave.buf, "a+");
if (!f)
msg->showMsgBox(UserMsgBox::M_OK, "certificate save failed",
"Could not save the certificate");
else {
fprintf(f, "%s\n", out_buf);
fclose(f);
}
/* New host */
if (err == GNUTLS_E_NO_CERTIFICATE_FOUND) {
CharArray text;

vlog.debug("Server host not previously known");
vlog.debug("%s", info.data);

text.format("This certificate has been signed by an unknown "
"authority:\n\n%s\n\nSomeone could be trying to "
"impersonate the site and you should not "
"continue.\n\nDo you want to make an exception "
"for this server?", info.data);

if (!msg->showMsgBox(UserMsgBox::M_YESNO,
"Unknown certificate issuer",
text.buf))
throw AuthFailureException("Unknown certificate issuer");
} else if (err == GNUTLS_E_CERTIFICATE_KEY_MISMATCH) {
CharArray text;

vlog.debug("Server host key mismatch");
vlog.debug("%s", info.data);

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\nSomeone "
"could be trying to impersonate the site and you "
"should not continue.\n\nDo you want to make an "
"exception for this server?", info.data);

if (!msg->showMsgBox(UserMsgBox::M_YESNO,
"Unexpected server certificate",
text.buf))
throw AuthFailureException("Unexpected server certificate");
} }


delete [] out_buf;
if (gnutls_store_pubkey(dbPath.buf, NULL, client->getServerName(),
NULL, GNUTLS_CRT_X509, &cert_list[0], 0, 0))
vlog.error("Failed to store server certificate to known hosts database");


gnutls_x509_crt_deinit(crt); gnutls_x509_crt_deinit(crt);
/* /*

Laden…
Annuleren
Opslaan