anflext.com

Add feature to TigerVNC vncviewer: X509 client authentication

I used KVM libvirt server with TLS by VNCTLSSetup.

According as the document, VNC clients to work with the feature is limited. And I used some worked clients well. But I like use TigerVNC vncviewer.

So I try to find the way to add X509 client authentication feature. First I find the request of X509 client authentication in github.

Then I check the source code. I find TigerVNC vncviewer is very flexible. And add several lines code to add the feature. The key is calling the function gnutls_certificate_set_x509_key_file. The feature look like screenshot below:

The patch below has committed the pull request to github:

diff --git a/common/rfb/CSecurityTLS.cxx b/common/rfb/CSecurityTLS.cxx
index 6eeb6a84..96c0ad14 100644
--- a/common/rfb/CSecurityTLS.cxx
+++ b/common/rfb/CSecurityTLS.cxx
@@ -56,6 +56,12 @@ StringParameter CSecurityTLS::X509CA("X509CA", "X509 CA certificate",
 StringParameter CSecurityTLS::X509CRL("X509CRL", "X509 CRL file",
                                      configdirfn("x509_crl.pem"),
                                      ConfViewer);
+StringParameter CSecurityTLS::X509CERT("X509Cert", "X509 client certificate",
+                                     configdirfn("x509_cert.pem"),
+                                     ConfViewer);
+StringParameter CSecurityTLS::X509KEY("X509Key", "X509 client private key",
+                                     configdirfn("x509_key.pem"),
+                                     ConfViewer);
 
 static LogWriter vlog("TLS");
 
@@ -281,6 +287,9 @@ void CSecurityTLS::setParam()
     if (gnutls_certificate_set_x509_crl_file(cert_cred, X509CRL, GNUTLS_X509_FMT_PEM) < 0)
       vlog.error("Could not load user specified certificate revocation list");
 
+    if (gnutls_certificate_set_x509_key_file (cert_cred, X509CERT, X509KEY, GNUTLS_X509_FMT_PEM) < 0)
+      vlog.error("Could not load user specified client certificate");
+
     ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cert_cred);
     if (ret != GNUTLS_E_SUCCESS)
       throw rdr::TLSException("gnutls_credentials_set()", ret);
diff --git a/common/rfb/CSecurityTLS.h b/common/rfb/CSecurityTLS.h
index 848ef9bb..d2634b6e 100644
--- a/common/rfb/CSecurityTLS.h
+++ b/common/rfb/CSecurityTLS.h
@@ -43,6 +43,8 @@ namespace rfb {
 
     static StringParameter X509CA;
     static StringParameter X509CRL;
+    static StringParameter X509CERT;
+    static StringParameter X509KEY;
 
   protected:
     void shutdown();
diff --git a/vncviewer/OptionsDialog.cxx b/vncviewer/OptionsDialog.cxx
index e04065ec..e4b1e789 100644
--- a/vncviewer/OptionsDialog.cxx
+++ b/vncviewer/OptionsDialog.cxx
@@ -61,7 +61,7 @@ std::map<OptionsCallback*, void*> OptionsDialog::callbacks;
 static std::set<OptionsDialog *> instances;
 
 OptionsDialog::OptionsDialog()
-  : Fl_Window(580, 420, _("TigerVNC Options"))
+  : Fl_Window(580, 450, _("TigerVNC Options"))
 {
   int x, y;
   Fl_Navigation *navigation;
@@ -305,6 +305,8 @@ void OptionsDialog::loadOptions(void)
 #ifdef HAVE_GNUTLS
   caInput->value(CSecurityTLS::X509CA);
   crlInput->value(CSecurityTLS::X509CRL);
+  certInput->value(CSecurityTLS::X509CERT);
+  keyInput->value(CSecurityTLS::X509KEY);
 
   handleX509(encX509Checkbox, this);
 #endif
@@ -436,6 +438,8 @@ void OptionsDialog::storeOptions(void)
 
   CSecurityTLS::X509CA.setParam(caInput->value());
   CSecurityTLS::X509CRL.setParam(crlInput->value());
+  CSecurityTLS::X509CERT.setParam(certInput->value());
+  CSecurityTLS::X509KEY.setParam(keyInput->value());
 #endif
 
 #ifdef HAVE_NETTLE
@@ -728,6 +732,20 @@ void OptionsDialog::createSecurityPage(int tx, int ty, int tw, int th)
                             _("Path to X509 CRL file"));
     crlInput->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
     ty += INPUT_HEIGHT + TIGHT_MARGIN;
+
+    ty += INPUT_LABEL_OFFSET;
+    certInput = new Fl_Input(tx + INDENT, ty,
+                           width - INDENT * 2, INPUT_HEIGHT,
+                           _("Path to X509 client certificate"));
+    certInput->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
+    ty += INPUT_HEIGHT + TIGHT_MARGIN;
+
+    ty += INPUT_LABEL_OFFSET;
+    keyInput = new Fl_Input(tx + INDENT, ty,
+                            width - INDENT * 2, INPUT_HEIGHT,
+                            _("Path to X509 client private key"));
+    keyInput->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
+    ty += INPUT_HEIGHT + TIGHT_MARGIN;
 #endif
 #ifdef HAVE_NETTLE
     encRSAAESCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
@@ -1094,9 +1112,13 @@ void OptionsDialog::handleX509(Fl_Widget* /*widget*/, void *data)
   if (dialog->encX509Checkbox->value()) {
     dialog->caInput->activate();
     dialog->crlInput->activate();
+    dialog->certInput->activate();
+    dialog->keyInput->activate();
   } else {
     dialog->caInput->deactivate();
     dialog->crlInput->deactivate();
+    dialog->certInput->deactivate();
+    dialog->keyInput->deactivate();
   }
 }
 
diff --git a/vncviewer/OptionsDialog.h b/vncviewer/OptionsDialog.h
index f6ca89b1..b19e0559 100644
--- a/vncviewer/OptionsDialog.h
+++ b/vncviewer/OptionsDialog.h
@@ -105,6 +105,8 @@ protected:
   Fl_Check_Button *encRSAAESCheckbox;
   Fl_Input *caInput;
   Fl_Input *crlInput;
+  Fl_Input *certInput;
+  Fl_Input *keyInput;
 
   Fl_Group *authenticationGroup;
   Fl_Check_Button *authNoneCheckbox;
diff --git a/vncviewer/parameters.cxx b/vncviewer/parameters.cxx
index 4bbf7a7f..d76c47d0 100644
--- a/vncviewer/parameters.cxx
+++ b/vncviewer/parameters.cxx
@@ -178,6 +178,8 @@ static VoidParameter* parameterArray[] = {
 #ifdef HAVE_GNUTLS
   &CSecurityTLS::X509CA,
   &CSecurityTLS::X509CRL,
+  &CSecurityTLS::X509CERT,
+  &CSecurityTLS::X509KEY,
 #endif // HAVE_GNUTLS
   &SecurityClient::secTypes,
   /* Misc. */
diff --git a/vncviewer/vncviewer.man b/vncviewer/vncviewer.man
index 79c410ae..80508d56 100644
--- a/vncviewer/vncviewer.man
+++ b/vncviewer/vncviewer.man
@@ -165,6 +165,20 @@ Path to certificate revocation list to use in conjunction with
 \fI~/.config/tigervnc/x509_crl.pem\fP.
 .
 .TP
+.B \-X509Cert \fIpath\fP
+Path to client certificate to use when authenticating client using any
+of the X509 security schemes (X509None, X509Vnc, etc.). Must be in PEM
+format. Default is \fI$XDG_CONFIG_HOME/tigervnc/x509_cert.pem\fP, or
+\fI~/.config/tigervnc/x509_cert.pem\fP.
+.
+.TP
+.B \-X509Key \fIpath\fP
+Path to client private key to use in conjunction with
+\fB-X509Cert\fP. Must also be in PEM format. Default is
+\fI$XDG_CONFIG_HOME/tigervnc/x509_key.pem\fP, or
+\fI~/.config/tigervnc/x509_key.pem\fP.
+.
+.TP
 .B \-Shared
 When you make a connection to a VNC server, all other existing connections are
 normally closed.  This option requests that they be left open, allowing you to

My pull request is not merged to master. So if you want to use the feateure, you can apply the patch and recomiple the codes.

22