From cc654987ffe4694eda3cc6437ad5c1d054b19d3c Mon Sep 17 00:00:00 2001
From: Martin Koegler <mkoegler@auto.tuwien.ac.at>
Date: Tue, 2 Dec 2008 16:45:45 +0100
Subject: [PATCH] Implement client side of tightvnc Sectype + TLS Tunnels

---
 common/rfb/CConnection.cxx |  121 ++++++++++++++++++++++++++++++++++++++++++++
 common/rfb/CConnection.h   |   18 +++++++
 unix/vncviewer/CConn.cxx   |   29 +++++++++++
 unix/vncviewer/CConn.h     |    7 +++
 unix/vncviewer/Makefile.am |    2 +-
 5 files changed, 176 insertions(+), 1 deletions(-)

diff --git a/common/rfb/CConnection.cxx b/common/rfb/CConnection.cxx
index 36778f0..132d28c 100644
--- a/common/rfb/CConnection.cxx
+++ b/common/rfb/CConnection.cxx
@@ -31,11 +31,36 @@ using namespace rfb;
 
 static LogWriter vlog("CConnection");
 
+#define TightVncVendor "TGHT"
+#define StandardVendor "STDV"
+
+#define SigAuthNone "NOAUTH__"
+#define SigAuthVNC "VNCAUTH_"
+
+#define SigNoTunneling "NOTUNNEL"
+#define SigTLSTunneling "TLSTUNNL"
+#define SigX509Tunneling "X509TUNL"
+
 CConnection::CConnection()
   : is(0), os(0), reader_(0), writer_(0),
     shared(false), security(0), nSecTypes(0), clientSecTypeOrder(false),
     state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false)
 {
+      // Supported tunnel methods
+    tunnelCaps.add(tunnelTypeNone, TightVncVendor, SigNoTunneling,
+		 "No tunneling");
+#ifdef HAVE_GNUTLS
+    tunnelCaps.add(tunnelTypeTLS, TightVncVendor, SigTLSTunneling,
+		 "TLS tunneling");
+    tunnelCaps.add(tunnelTypeX509, TightVncVendor, SigX509Tunneling,
+		 "X509 tunneling");
+#endif
+
+    // Supported authentication methods
+    authCaps.add(secTypeNone, StandardVendor, SigAuthNone,
+		 "No authentication");
+    authCaps.add(secTypeVncAuth, StandardVendor, SigAuthVNC,
+		 "Standard VNC password authentication");
 }
 
 CConnection::~CConnection()
@@ -81,6 +106,9 @@ void CConnection::processMsg()
   case RFBSTATE_PROTOCOL_VERSION: processVersionMsg();       break;
   case RFBSTATE_SECURITY_TYPES:   processSecurityTypesMsg(); break;
   case RFBSTATE_SECURITY:         processSecurityMsg();      break;
+  case RFBSTATE_TIGHT_TUNN_TYPE:  processTunnelTypeMsg();   break;
+  case RFBSTATE_TIGHT_TUNN:       processTunnelMsg();       break;
+  case RFBSTATE_TIGHT_AUTH_TYPE:  processAuthTypeMsg();     break;
   case RFBSTATE_SECURITY_RESULT:  processSecurityResultMsg(); break;
   case RFBSTATE_INITIALISATION:   processInitMsg();          break;
   case RFBSTATE_NORMAL:           reader_->readMsg();        break;
@@ -165,6 +193,15 @@ void CConnection::processSecurityTypesMsg()
       vlog.debug("Server offers security type %s(%d)",
                  secTypeName(serverSecType),serverSecType);
 
+      if (serverSecType == secTypeTight) {
+	vlog.info("Enabling TightVNC protocol extensions");
+	os->writeU8(serverSecType);
+	os->flush();
+	cp.tightExtensionsEnabled = true;
+	state_ = RFBSTATE_TIGHT_TUNN_TYPE;
+	processTunnelTypeMsg();
+	return;
+      }
       // If we haven't already chosen a secType, try this one
       // If we are using the client's preference for types,
       // we keep trying types, to find the one that matches and
@@ -200,6 +237,81 @@ void CConnection::processSecurityTypesMsg()
   processSecurityMsg();
 }
 
+#include <stdio.h>
+void CConnection::readCapabilityList(CapsContainer &caps, int count)
+{
+  int i;
+  CapabilityInfo info;
+  for (i = 0; i < count; i++) {
+    info.code = is->readU32();
+    is->readBytes(info.vendorSignature, 4);
+    is->readBytes(info.nameSignature, 8);
+    caps.enable(&info);
+  }
+}
+
+
+void CConnection::processTunnelTypeMsg()
+{
+  int tunnelType;
+  int nTunnelTypes = is->readU32();
+
+  vlog.info("Selecting tunnel type");
+  tunnel = 0;
+  if (nTunnelTypes) {
+
+    readCapabilityList(tunnelCaps, nTunnelTypes);
+    if(tunnelCaps.numEnabled()==0)
+      throw Exception("No matching tunnel types");
+
+    tunnelType = tunnelCaps.getByOrder(0);
+    os->writeU32(tunnelType);
+    os->flush();
+    
+   tunnel = getTunnel(tunnelType);
+  }
+
+  if(tunnel) {
+    state_ = RFBSTATE_TIGHT_TUNN;
+    processTunnelMsg();
+  } else {
+    state_ = RFBSTATE_TIGHT_AUTH_TYPE;
+    processAuthTypeMsg();
+  }
+}
+
+void CConnection::processTunnelMsg()
+{
+  vlog.debug("processing tunnel message");
+  if (!tunnel || tunnel->processMsg(this)) {
+    state_ = RFBSTATE_TIGHT_AUTH_TYPE;
+    processAuthTypeMsg();
+  }
+}
+
+void CConnection::processAuthTypeMsg()
+{
+  int secType;
+  vlog.info("Selecting authentification type");
+  int nAuthTypes = is->readU32();
+  if (nAuthTypes == 0) 
+    secType = secTypeNone;
+  else {
+    readCapabilityList(authCaps, nAuthTypes);
+    
+    if(authCaps.numEnabled()==0)
+      throw Exception("No suitable authentication scheme found");
+    
+    secType = authCaps.getByOrder(0);
+    os->writeU32(secType);
+    os->flush();
+  }
+
+  state_ = RFBSTATE_SECURITY;
+  security = getCSecurity(secType);
+  processSecurityMsg();
+}
+
 void CConnection::processSecurityMsg()
 {
   vlog.debug("processing security message");
@@ -245,6 +357,15 @@ void CConnection::processInitMsg()
 {
   vlog.debug("reading server initialisation");
   reader_->readServerInit();
+  if (cp.tightExtensionsEnabled) {
+    int nServerMessageTypes = is->readU16();
+    int nClientMessageTypes = is->readU16();
+    int nEncodingTypes = is->readU16();
+    is->readU16();
+    readCapabilityList(serverMsgCaps, nServerMessageTypes);
+    readCapabilityList(clientMsgCaps, nClientMessageTypes);
+    readCapabilityList(encodingCaps, nEncodingTypes);
+  }
 }
 
 void CConnection::throwConnFailedException()
diff --git a/common/rfb/CConnection.h b/common/rfb/CConnection.h
index 79110eb..f413146 100644
--- a/common/rfb/CConnection.h
+++ b/common/rfb/CConnection.h
@@ -23,9 +23,11 @@
 #ifndef __RFB_CCONNECTION_H__
 #define __RFB_CCONNECTION_H__
 
+#include <rfb/CapsContainer.h>
 #include <rdr/InStream.h>
 #include <rdr/OutStream.h>
 #include <rfb/CMsgHandler.h>
+#include <rfb/CTunnel.h>
 #include <rfb/util.h>
 
 namespace rfb {
@@ -140,6 +142,9 @@ namespace rfb {
       RFBSTATE_UNINITIALISED,
       RFBSTATE_PROTOCOL_VERSION,
       RFBSTATE_SECURITY_TYPES,
+      RFBSTATE_TIGHT_TUNN_TYPE,
+      RFBSTATE_TIGHT_TUNN,
+      RFBSTATE_TIGHT_AUTH_TYPE,
       RFBSTATE_SECURITY,
       RFBSTATE_SECURITY_RESULT,
       RFBSTATE_INITIALISATION,
@@ -155,6 +160,9 @@ namespace rfb {
   private:
     void processVersionMsg();
     void processSecurityTypesMsg();
+    void processTunnelTypeMsg();
+    void processTunnelMsg();
+    void processAuthTypeMsg();
     void processSecurityMsg();
     void processSecurityResultMsg();
     void processInitMsg();
@@ -162,6 +170,9 @@ namespace rfb {
     void throwConnFailedException();
     void securityCompleted();
 
+    void readCapabilityList(CapsContainer &caps, int count);
+    virtual CTunnel* getTunnel(int tunnelType) = 0;
+
     rdr::InStream* is;
     rdr::OutStream* os;
     CMsgReader* reader_;
@@ -178,6 +189,13 @@ namespace rfb {
     CharArray serverName;
 
     bool useProtocol3_3;
+
+    CapsContainer tunnelCaps;
+    CapsContainer authCaps;
+    CapsContainer serverMsgCaps;
+    CapsContainer clientMsgCaps;
+    CapsContainer encodingCaps;
+    CTunnel* tunnel;
   };
 }
 #endif
diff --git a/unix/vncviewer/CConn.cxx b/unix/vncviewer/CConn.cxx
index cb33066..e4730e1 100644
--- a/unix/vncviewer/CConn.cxx
+++ b/unix/vncviewer/CConn.cxx
@@ -26,6 +26,8 @@
 #include <rfb/secTypes.h>
 #include <rfb/CSecurityNone.h>
 #include <rfb/CSecurityVncAuth.h>
+#include <rfb/CTunnelTLS.h>
+#include <rfb/CTunnelX509.h>
 #include <rfb/Hostname.h>
 #include <rfb/LogWriter.h>
 #include <rfb/util.h>
@@ -49,6 +51,11 @@ StringParameter menuKey("MenuKey", "The key which brings up the popup menu",
                         "F8");
 StringParameter windowName("name", "The X window name", "");
 
+#ifdef HAVE_GNUTLS
+StringParameter x509ca("x509ca", "X509 CA certificate", "");
+StringParameter x509crl("x509crl", "X509 CRL file", "");
+#endif
+
 CConn::CConn(Display* dpy_, int argc_, char** argv_, network::Socket* sock_,
              char* vncServerName, bool reverse)
   : dpy(dpy_), argc(argc_),
@@ -747,3 +754,25 @@ void CConn::requestNewUpdate()
                                           !formatChange);
   formatChange = false;
 }
+
+CTunnel* CConn::getTunnel(int tunnelType)
+{
+#ifdef HAVE_GNUTLS
+  if (tunnelType == tunnelTypeTLS)
+    return new CTunnelTLS();
+  if (tunnelType == tunnelTypeX509)
+    return new CTunnelX509(this, x509ca.getData(), x509crl.getData());
+#endif
+  return NULL;
+}
+
+bool CConn::showMsgBox(int flags,const char* text)
+{
+  const char* secType = secTypeName(getCurrentCSecurity()->getType());
+  const char* titlePrefix = "VNC Authentication";
+  CharArray title(strlen(titlePrefix) + strlen(secType) + 4);
+  sprintf(title.buf, "%s [%s]", titlePrefix, secType);
+
+  TXMsgBox msgBox(dpy,text,flags,title.buf);
+  return msgBox.show();
+}
diff --git a/unix/vncviewer/CConn.h b/unix/vncviewer/CConn.h
index a81af48..68e1cc1 100644
--- a/unix/vncviewer/CConn.h
+++ b/unix/vncviewer/CConn.h
@@ -25,6 +25,7 @@
 #include <rfb/CConnection.h>
 #include <rfb/Exception.h>
 #include <rfb/UserPasswdGetter.h>
+#include <rfb/UserMsgBox.h>
 #include <rdr/FdInStream.h>
 #include <list>
 
@@ -40,6 +41,7 @@ class DesktopWindow;
 namespace network { class Socket; }
 
 class CConn : public rfb::CConnection, public rfb::UserPasswdGetter,
+              public rfb::UserMsgBox,
               public TXDeleteWindowCallback,
               public rdr::FdInStreamBlockCallback,
               public TXMenuCallback , public OptionsDialogCallback,
@@ -60,6 +62,9 @@ public:
   // UserPasswdGetter methods
   virtual void getUserPasswd(char** user, char** password);
 
+  // UserMsgBox methods
+  virtual bool showMsgBox(int flags,const char* text);
+
   // TXMenuCallback methods
   void menuSelect(long id, TXMenu* m);
 
@@ -96,6 +101,8 @@ private:
   void checkEncodings();
   void requestNewUpdate();
 
+  rfb::CTunnel* getTunnel(int tunnelType);
+
   Display* dpy;
   int argc;
   char** argv;
diff --git a/unix/vncviewer/Makefile.am b/unix/vncviewer/Makefile.am
index 6b81ec3..b6088c3 100644
--- a/unix/vncviewer/Makefile.am
+++ b/unix/vncviewer/Makefile.am
@@ -13,6 +13,6 @@ vncviewer_CPPFLAGS = -I$(COMMON_DIR) -I$(top_srcdir) -I$(top_srcdir)/tx \
 
 vncviewer_LDADD = $(top_srcdir)/tx/libtx.la $(COMMON_DIR)/rfb/librfb.la \
 	$(COMMON_DIR)/network/libnetwork.la $(COMMON_DIR)/rdr/librdr.la \
-	@X_PRE_LIBS@ @X_LIBS@ -lXext -lX11 @X_EXTRA_LIBS@ # @LIBINTL@
+	@X_PRE_LIBS@ @X_LIBS@ -lXext -lX11 @X_EXTRA_LIBS@ @LIBGNUTLS_LIBS@ # @LIBINTL@
 
 EXTRA_DIST = vncviewer.man
-- 
1.5.6.5

