Merge branch 'vncserverfix' of https://github.com/michalsrb/tigervnc
diff --git a/java/com/tigervnc/rfb/CSecurityTLS.java b/java/com/tigervnc/rfb/CSecurityTLS.java
index e7510c8..a8f6df3 100644
--- a/java/com/tigervnc/rfb/CSecurityTLS.java
+++ b/java/com/tigervnc/rfb/CSecurityTLS.java
@@ -33,6 +33,7 @@
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStream;
@@ -42,6 +43,12 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+import javax.net.ssl.HostnameVerifier;
import javax.swing.JOptionPane;
import javax.xml.bind.DatatypeConverter;
@@ -60,23 +67,17 @@
private void initGlobal()
{
- boolean globalInitDone = false;
-
- if (!globalInitDone) {
- try {
- ctx = SSLContext.getInstance("TLS");
- } catch(NoSuchAlgorithmException e) {
- throw new Exception(e.toString());
- }
-
- globalInitDone = true;
+ try {
+ ctx = SSLContext.getInstance("TLS");
+ } catch(NoSuchAlgorithmException e) {
+ throw new Exception(e.toString());
}
}
public CSecurityTLS(boolean _anon)
{
anon = _anon;
- session = null;
+ manager = null;
setDefaults();
cafile = x509ca.getData();
@@ -116,7 +117,7 @@
initGlobal();
- if (session == null) {
+ if (manager == null) {
if (!is.checkNoWait(1))
return false;
@@ -132,21 +133,15 @@
}
setParam();
-
}
try {
manager = new SSLEngineManager(engine, is, os);
manager.doHandshake();
} catch(java.lang.Exception e) {
- if (e.getMessage().equals("X.509 certificate not trusted"))
- throw new WarningException(e.getMessage());
- else
- throw new SystemException(e.toString());
+ throw new SystemException(e.toString());
}
- //checkSession();
-
cc.setStreams(new TLSInStream(is, manager),
new TLSOutStream(os, manager));
return true;
@@ -199,13 +194,6 @@
}
- class MyHandshakeListener implements HandshakeCompletedListener {
- public void handshakeCompleted(HandshakeCompletedEvent e) {
- vlog.info("Handshake succesful!");
- vlog.info("Using cipher suite: " + e.getCipherSuite());
- }
- }
-
class MyX509TrustManager implements X509TrustManager
{
@@ -263,7 +251,7 @@
tmf.init(new CertPathTrustManagerParameters(params));
tm = (X509TrustManager)tmf.getTrustManagers()[0];
} catch (java.lang.Exception e) {
- vlog.error(e.toString());
+ throw new Exception(e.getMessage());
}
}
@@ -279,8 +267,9 @@
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA-1");
+ verifyHostname(chain[0]);
tm.checkServerTrusted(chain, authType);
- } catch (CertificateException e) {
+ } catch (java.lang.Exception e) {
if (e.getCause() instanceof CertPathBuilderException) {
Object[] answer = {"YES", "NO"};
X509Certificate cert = chain[0];
@@ -305,19 +294,19 @@
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
null, answer, answer[0]);
if (ret == JOptionPane.YES_OPTION) {
- File vncDir = new File(FileUtils.getVncHomeDir());
- if (!vncDir.exists() && !vncDir.mkdir()) {
- vlog.info("Certificate save failed, unable to create ~/.vnc");
- return;
- }
Collection<? extends X509Certificate> cacerts = null;
- String castore =
- FileUtils.getVncHomeDir()+"x509_savedcerts.pem";
- File caFile = new File(castore);
+ File vncDir = new File(FileUtils.getVncHomeDir());
+ File caFile = new File(vncDir, "x509_savedcerts.pem");
try {
- caFile.createNewFile();
- } catch (IOException ioe) {
- vlog.error(ioe.getCause().getMessage());
+ if (!vncDir.exists())
+ vncDir.mkdir();
+ if (!caFile.createNewFile()) {
+ vlog.error("Certificate save failed.");
+ return;
+ }
+ } catch (java.lang.Exception ioe) {
+ // skip save if security settings prohibit access to filesystem
+ vlog.error("Certificate save failed: "+ioe.getMessage());
return;
}
InputStream caStream = new MyFileInputStream(caFile);
@@ -332,30 +321,28 @@
pem = pem.replaceAll("(.{64})", "$1\n");
FileWriter fw = null;
try {
- fw = new FileWriter(castore, true);
+ fw = new FileWriter(caFile.getAbsolutePath(), true);
fw.write("-----BEGIN CERTIFICATE-----\n");
fw.write(pem+"\n");
fw.write("-----END CERTIFICATE-----\n");
} catch (IOException ioe) {
- throw new Exception(ioe.getCause().getMessage());
+ throw new Exception(ioe.getMessage());
} finally {
try {
if (fw != null)
fw.close();
} catch(IOException ioe2) {
- throw new Exception(ioe2.getCause().getMessage());
+ throw new Exception(ioe2.getMessage());
}
}
}
}
} else {
- throw new WarningException("X.509 certificate not trusted");
+ throw new WarningException("Peer certificate verification failed.");
}
} else {
- throw new SystemException(e.getCause().getMessage());
+ throw new SystemException(e.getMessage());
}
- } catch (java.lang.Exception e) {
- throw new Exception(e.getCause().getMessage());
}
}
@@ -364,6 +351,53 @@
return tm.getAcceptedIssuers();
}
+ private void verifyHostname(X509Certificate cert)
+ throws CertificateParsingException
+ {
+ try {
+ Collection sans = cert.getSubjectAlternativeNames();
+ if (sans == null) {
+ String dn = cert.getSubjectX500Principal().getName();
+ LdapName ln = new LdapName(dn);
+ for (Rdn rdn : ln.getRdns()) {
+ if (rdn.getType().equalsIgnoreCase("CN")) {
+ String peer =
+ ((CConn)client).getSocket().getPeerName().toLowerCase();
+ if (peer.equals(((String)rdn.getValue()).toLowerCase()))
+ return;
+ }
+ }
+ } else {
+ Iterator i = sans.iterator();
+ while (i.hasNext()) {
+ List nxt = (List)i.next();
+ if (((Integer)nxt.get(0)).intValue() == 2) {
+ String peer =
+ ((CConn)client).getSocket().getPeerName().toLowerCase();
+ if (peer.equals(((String)nxt.get(1)).toLowerCase()))
+ return;
+ } else if (((Integer)nxt.get(0)).intValue() == 7) {
+ String peer = ((CConn)client).getSocket().getPeerAddress();
+ if (peer.equals(((String)nxt.get(1)).toLowerCase()))
+ return;
+ }
+ }
+ }
+ Object[] answer = {"YES", "NO"};
+ int ret = JOptionPane.showOptionDialog(null,
+ "Hostname verification failed. Do you want to continue?",
+ "Hostname Verification Failure",
+ JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
+ null, answer, answer[0]);
+ if (ret != JOptionPane.YES_OPTION)
+ throw new WarningException("Hostname verification failed.");
+ } catch (CertificateParsingException e) {
+ throw new SystemException(e.getMessage());
+ } catch (InvalidNameException e) {
+ throw new SystemException(e.getMessage());
+ }
+ }
+
private class MyFileInputStream extends InputStream {
// Blank lines in a certificate file will cause Java 6 to throw a
// "DerInputStream.getLength(): lengthTag=127, too big" exception.
@@ -390,7 +424,7 @@
if (reader != null)
reader.close();
} catch(IOException ioe) {
- throw new Exception(ioe.getCause().getMessage());
+ throw new Exception(ioe.getMessage());
}
}
Charset utf8 = Charset.forName("UTF-8");
@@ -412,7 +446,6 @@
return len;
}
-
@Override
public int read() throws IOException {
if (!buf.hasRemaining())
@@ -426,13 +459,9 @@
public final String description()
{ return anon ? "TLS Encryption without VncAuth" : "X509 Encryption without VncAuth"; }
- //protected void checkSession();
protected CConnection client;
-
-
private SSLContext ctx;
- private SSLSession session;
private SSLEngine engine;
private SSLEngineManager manager;
private boolean anon;
diff --git a/java/com/tigervnc/vncviewer/CConn.java b/java/com/tigervnc/vncviewer/CConn.java
index 88b25ec..f592cb5 100644
--- a/java/com/tigervnc/vncviewer/CConn.java
+++ b/java/com/tigervnc/vncviewer/CConn.java
@@ -1411,6 +1411,10 @@
public void actionPerformed(ActionEvent e) {}
+ public Socket getSocket() {
+ return sock;
+ }
+
////////////////////////////////////////////////////////////////////
// The following methods are called from both RFB and GUI threads
diff --git a/java/com/tigervnc/vncviewer/VncViewer.java b/java/com/tigervnc/vncviewer/VncViewer.java
index 0f7ce8e..0c54d79 100644
--- a/java/com/tigervnc/vncviewer/VncViewer.java
+++ b/java/com/tigervnc/vncviewer/VncViewer.java
@@ -464,19 +464,18 @@
cc = new CConn(this, sock, vncServerName.getValue());
while (!cc.shuttingDown)
cc.processMsg();
+ exit(0);
} catch (java.lang.Exception e) {
if (cc == null || !cc.shuttingDown) {
reportException(e);
if (cc != null)
cc.deleteWindow();
- exit(1);
} else if (embed.getValue()) {
reportException(new java.lang.Exception("Connection closed"));
- } else {
- cc = null;
+ exit(0);
}
+ exit(1);
}
- exit(0);
}
static BoolParameter noLionFS