Browse Source

[Development] client: Add dialog window to accept/save invalid X509

certificates. (Guillaume Destuynder)


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4198 3789f03b-4d11-0410-bbf8-ca57d06f2519
tags/v1.0.90
Adam Tkac 13 years ago
parent
commit
27b2f773be

+ 118
- 8
common/rfb/CSecurityTLS.cxx View File

@@ -2,6 +2,7 @@
* Copyright (C) 2004 Red Hat Inc.
* Copyright (C) 2005 Martin Koegler
* Copyright (C) 2010 TigerVNC Team
* Copyright (C) 2010 m-privacy GmbH
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,13 +28,18 @@
#error "This header should not be compiled without HAVE_GNUTLS defined"
#endif

#include <stdlib.h>
#include <unistd.h>

#include <rfb/CSecurityTLS.h>
#include <rfb/SSecurityVeNCrypt.h>
#include <rfb/CConnection.h>
#include <rfb/LogWriter.h>
#include <rfb/Exception.h>
#include <rfb/UserMsgBox.h>
#include <rdr/TLSInStream.h>
#include <rdr/TLSOutStream.h>
#include <os/os.h>

#include <gnutls/x509.h>

@@ -76,6 +82,26 @@ CSecurityTLS::CSecurityTLS(bool _anon) : session(0), anon_cred(0),
crlfile = x509crl.getData();
}

void CSecurityTLS::setDefaults()
{
char* homeDir = NULL;

if (gethomedir(&homeDir) == -1) {
vlog.error("Could not obtain home directory path");
return;
}

CharArray caDefault(strlen(homeDir)+17);
sprintf(caDefault.buf, "%s/.vnc/x509_certs", homeDir);
delete [] homeDir;

/* XXX Do we need access() check here? */
if (!access(caDefault.buf, R_OK))
x509ca.setDefaultStr(strdup(caDefault.buf));
else
vlog.error("Failed to open ~/.vnc/x509_certs");
}

void CSecurityTLS::shutdown()
{
if (session)
@@ -206,6 +232,7 @@ void CSecurityTLS::checkSession()
const gnutls_datum *cert_list;
unsigned int cert_list_size = 0;
unsigned int i;
gnutls_datum_t info;

if (anon)
return;
@@ -226,25 +253,108 @@ void CSecurityTLS::checkSession()
throw AuthFailureException("certificate verification failed");
}

if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
throw AuthFailureException("certificate issuer unknown");
if (status & GNUTLS_CERT_REVOKED) {
throw AuthFailureException("certificate has been revoked");
}

if (status & GNUTLS_CERT_INVALID)
throw AuthFailureException("certificate not trusted");
if (status & GNUTLS_CERT_NOT_ACTIVATED) {
throw AuthFailureException("certificate has not been activated");
}

for (i = 0; i < cert_list_size; i++) {
gnutls_x509_crt crt;
gnutls_x509_crt_init(&crt);

if (gnutls_x509_crt_import(crt, &cert_list[i],GNUTLS_X509_FMT_DER) < 0)
throw AuthFailureException("Decoding of certificate failed");
throw AuthFailureException("decoding of certificate failed");

if (gnutls_x509_crt_print(crt, GNUTLS_CRT_PRINT_ONELINE, &info)) {
gnutls_free(info.data);
throw AuthFailureException("Could not find certificate to display");
}

if (gnutls_x509_crt_check_hostname(crt, client->getServerName()) == 0) {
#if 0
throw AuthFailureException("Hostname mismatch"); /* Non-fatal for now... */
#endif
char buf[255];
sprintf(buf, "Hostname (%s) does not match any certificate, do you want to continue?", client->getServerName());
vlog.debug("hostname mismatch");
if(!msg->showMsgBox(UserMsgBox::M_YESNO, "hostname mismatch", buf))
throw AuthFailureException("hostname mismatch");
}

if (status & GNUTLS_CERT_EXPIRED) {
vlog.debug("certificate has expired");
if (!msg->showMsgBox(UserMsgBox::M_YESNO, "certficate has expired", "The certificate of the server has expired, do you want to continue?"))
throw AuthFailureException("certificate has expired");
}

if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
size_t out_size;
char *homeDir = NULL;
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)
AuthFailureException("certificate decoding error");

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

certinfo = new char[len];
if (certinfo == NULL)
throw AuthFailureException("Out of memory");

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);

for (int i = 0; i < len - 1; i++)
if (certinfo[i] == ',' && certinfo[i + 1] == ' ')
certinfo[i] = '\n';

if (!msg->showMsgBox(UserMsgBox::M_YESNO, "certificate issuer unknown",
certinfo)) {
delete [] certinfo;
throw AuthFailureException("certificate issuer unknown");
}
delete [] certinfo;

if (gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, NULL, &out_size)
== GNUTLS_E_SHORT_MEMORY_BUFFER)
AuthFailureException("Out of memory");

// Save cert
out_buf = new char[out_size];
if (out_buf == NULL)
AuthFailureException("Out of memory");

if (gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, out_buf, &out_size)
< 0)
AuthFailureException("certificate issuer unknown, and certificate "
"export failed");
if (gethomedir(&homeDir) == -1)
vlog.error("Could not obtain home directory path");
else {
FILE *f;
CharArray caSave(strlen(homeDir)+17);
sprintf(caSave.buf, "%s/.vnc/x509_certs", 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);
}
}
delete [] out_buf;
} else if (status & GNUTLS_CERT_INVALID)
throw AuthFailureException("certificate not trusted");

gnutls_x509_crt_deinit(crt);
gnutls_free(info.data);
}
}


+ 4
- 0
common/rfb/CSecurityTLS.h View File

@@ -33,11 +33,13 @@
#include <rfb/CSecurity.h>
#include <rfb/SSecurityVeNCrypt.h>
#include <rfb/Security.h>
#include <rfb/UserMsgBox.h>
#include <rdr/InStream.h>
#include <rdr/OutStream.h>
#include <gnutls/gnutls.h>

namespace rfb {
class UserMsgBox;
class CSecurityTLS : public CSecurity {
public:
CSecurityTLS(bool _anon);
@@ -46,9 +48,11 @@ namespace rfb {
virtual int getType() const { return anon ? secTypeTLSNone : secTypeX509None; }
virtual const char* description() const
{ return anon ? "TLS Encryption without VncAuth" : "X509 Encryption without VncAuth"; }
static void setDefaults();

static StringParameter x509ca;
static StringParameter x509crl;
static UserMsgBox *msg;

protected:
void shutdown();

+ 12
- 0
common/rfb/SecurityClient.cxx View File

@@ -37,6 +37,9 @@ using namespace rdr;
using namespace rfb;

UserPasswdGetter *CSecurity::upg = NULL;
#ifdef HAVE_GNUTLS
UserMsgBox *CSecurityTLS::msg = NULL;
#endif

StringParameter SecurityClient::secTypes
("SecurityTypes",
@@ -51,6 +54,9 @@ ConfViewer);
CSecurity* SecurityClient::GetCSecurity(U32 secType)
{
assert (CSecurity::upg != NULL); /* (upg == NULL) means bug in the viewer */
#ifdef HAVE_GNUTLS
assert (CSecurityTLS::msg != NULL);
#endif

if (!IsSupported(secType))
goto bail;
@@ -86,3 +92,9 @@ bail:
throw Exception("Security type not supported");
}

void SecurityClient::setDefaults()
{
#ifdef HAVE_GNUTLS
CSecurityTLS::setDefaults();
#endif
}

+ 2
- 0
common/rfb/SecurityClient.h View File

@@ -35,6 +35,8 @@ namespace rfb {
/* Create client side CSecurity class instance */
CSecurity* GetCSecurity(rdr::U32 secType);

static void setDefaults(void);

static StringParameter secTypes;
};


+ 1
- 0
common/rfb/UserMsgBox.h View File

@@ -33,6 +33,7 @@ namespace rfb {
M_DEFBUTTON1 = 0,
M_DEFBUTTON2 = 0x100
};
/* TODO Implement as function with variable arguments */
virtual bool showMsgBox(int flags,const char* title, const char* text)=0;
};
}

+ 13
- 2
unix/vncviewer/CConn.cxx View File

@@ -75,7 +75,10 @@ CConn::CConn(Display* dpy_, int argc_, char** argv_, network::Socket* sock_,
menuKeysym = XStringToKeysym(menuKeyStr.buf);

setShared(shared);
CSecurity::upg = this; /* Security instance is created in CConnection costructor. */
CSecurity::upg = this; /* Security instance is created in CConnection constructor. */
#ifdef HAVE_GNUTLS
CSecurityTLS::msg = this;
#endif

CharArray encStr(preferredEncoding.getData());
int encNum = encodingNum(encStr.buf);
@@ -125,6 +128,15 @@ CConn::~CConn() {
delete sock;
}

bool CConn::showMsgBox(int flags, const char* title, const char* text)
{
CharArray titleText(strlen(title) + 12);
sprintf(titleText.buf, "VNC Viewer: %s", title);

TXMsgBox msgBox(dpy,text,flags,titleText.buf);
return msgBox.show();
}

// deleteWindow() is called when the user closes the desktop or menu windows.

void CConn::deleteWindow(TXWindow* w) {
@@ -232,7 +244,6 @@ void CConn::getUserPasswd(char** user, char** password)
*password = strDup(dlg.passwdEntry.getText());
}


// CConnection callback methods

// serverInit() is called when the serverInit message has been received. At

+ 5
- 1
unix/vncviewer/CConn.h View File

@@ -26,6 +26,7 @@
#include <rfb/CConnection.h>
#include <rfb/Exception.h>
#include <rfb/UserPasswdGetter.h>
#include <rfb/UserMsgBox.h>
#include <rdr/FdInStream.h>
#include <list>

@@ -44,7 +45,7 @@ class CConn : public rfb::CConnection, public rfb::UserPasswdGetter,
public TXDeleteWindowCallback,
public rdr::FdInStreamBlockCallback,
public TXMenuCallback , public OptionsDialogCallback,
public TXEventHandler
public TXEventHandler, public rfb::UserMsgBox
{
public:

@@ -61,6 +62,9 @@ public:
// UserPasswdGetter methods
virtual void getUserPasswd(char** user, char** password);

// UserMsgBox methods
virtual bool showMsgBox(int flags, const char* title, const char* text);

// TXMenuCallback methods
void menuSelect(long id, TXMenu* m);


+ 3
- 0
unix/vncviewer/vncviewer.cxx View File

@@ -33,6 +33,7 @@
#include <locale.h>
#include <os/os.h>
#include <rfb/Logger_stdio.h>
#include <rfb/SecurityClient.h>
#include <rfb/LogWriter.h>
#include <network/TcpSocket.h>
#include "TXWindow.h"
@@ -278,6 +279,8 @@ int main(int argc, char** argv)
"Copyright (C) 2004-2009 Peter Astrand for Cendio AB\n"
"See http://www.tigervnc.org for information on TigerVNC.");

rfb::SecurityClient::setDefaults();

// Write about text to console, still using normal locale codeset
snprintf(aboutText, sizeof(aboutText),
gettext(englishAbout), PACKAGE_VERSION, buildtime);

Loading…
Cancel
Save