Merge branch 'fatal' of https://github.com/CendioOssman/tigervnc
diff --git a/contrib/packages/deb/ubuntu-trusty/debian/control b/contrib/packages/deb/ubuntu-trusty/debian/control
index 022f69f..f1cf906 100644
--- a/contrib/packages/deb/ubuntu-trusty/debian/control
+++ b/contrib/packages/deb/ubuntu-trusty/debian/control
@@ -3,13 +3,13 @@
 Priority: optional
 Maintainer: Brian P. Hinz <bphinz@users.sourceforge.net>
 Standards-Version: 3.8.4
-Build-Depends: debhelper (>> 7.1), zlib1g-dev, libjpeg-turbo8-dev, libxaw7-dev (>> 4.1.0), perl-modules, xfonts-base, xutils-dev, libx11-dev, libxau-dev, libxext-dev, libxi-dev, libxkbfile-dev, libxmu-dev, libxt-dev, x11proto-core-dev, cmake (>> 2.8), libgnutls28-dev, libpam0g-dev, libpng-dev, automake, autoconf, libtool, pkg-config, libpixman-1-dev, x11proto-bigreqs-dev, x11proto-composite-dev, x11proto-damage-dev, x11proto-dri2-dev, x11proto-fixes-dev, x11proto-fonts-dev, x11proto-gl-dev, x11proto-input-dev, x11proto-kb-dev, x11proto-randr-dev, x11proto-render-dev, x11proto-resource-dev, x11proto-scrnsaver-dev, x11proto-video-dev, x11proto-xext-dev, x11proto-xf86bigfont-dev, x11proto-xf86dga-dev, x11proto-xf86dri-dev, x11proto-xf86vidmode-dev, x11proto-xinerama-dev, libosmesa6-dev, libgl1-mesa-dev, libgl1-mesa-dri, libgl1-mesa-glx, libxfont-dev, x11proto-record-dev, default-jdk, libxtst-dev, libxft-dev, libexpat1-dev, libfontconfig1-dev, libxrender-dev, libt1-dev, libpciaccess-dev, curl, bzip2, quilt, libglu1-mesa-dev, libxcursor-dev, libxinerama-dev, libxfixes-dev, libcairo2-dev, x11proto-dri3-dev, libgcrypt20-dev, x11proto-xcmisc-dev, x11proto-present-dev
+Build-Depends: debhelper (>> 7.1), zlib1g-dev, libjpeg-turbo8-dev, libxaw7-dev (>> 4.1.0), perl-modules, xfonts-base, xutils-dev, libx11-dev, libxau-dev, libxext-dev, libxi-dev, libxkbfile-dev, libxmu-dev, libxt-dev, x11proto-core-dev, cmake (>> 2.8), libgnutls28-dev, libpam0g-dev, libpng-dev, automake, autoconf, libtool, pkg-config, libpixman-1-dev, x11proto-bigreqs-dev, x11proto-composite-dev, x11proto-damage-dev, x11proto-dri2-dev, x11proto-fixes-dev, x11proto-fonts-dev, x11proto-gl-dev, x11proto-input-dev, x11proto-kb-dev, x11proto-randr-dev, x11proto-render-dev, x11proto-resource-dev, x11proto-scrnsaver-dev, x11proto-video-dev, x11proto-xext-dev, x11proto-xf86bigfont-dev, x11proto-xf86dga-dev, x11proto-xf86dri-dev, x11proto-xf86vidmode-dev, x11proto-xinerama-dev, libosmesa6-dev, libgl1-mesa-dev, libgl1-mesa-dri, libgl1-mesa-glx, libxfont-dev, x11proto-record-dev, default-jdk, libxtst-dev, libxft-dev, libexpat1-dev, libfontconfig1-dev, libxrender-dev, libt1-dev, libpciaccess-dev, curl, bzip2, quilt, libglu1-mesa-dev, libxcursor-dev, libxinerama-dev, libxfixes-dev, libcairo2-dev, x11proto-dri3-dev, libgcrypt20-dev, x11proto-xcmisc-dev, x11proto-present-dev, libtasn1-dev
 Homepage: http://www.tigervnc.com
 
 Package: tigervncserver
 Architecture: any
 Provides: xserver, vnc-server
-Depends: x11-common | xserver-common, x11-utils, xauth, libbz2-1.0, libc6, libfontenc1, libfreetype6, libgcc1, libgl1-mesa-dri, libgnutls28, libjpeg-turbo8, libp11-kit0, libpam0g, libpixman-1-0, libstdc++6, libtasn1-3-bin, libx11-6, libxau6, libxcb1, libxdmcp6, libxext6, libxfont1, libxtst6, zlib1g, libglu1-mesa, libxcursor1, libxinerama1, libxfixes3, x11-xkb-utils, libgcrypt20
+Depends: x11-common | xserver-common, x11-utils, xauth, libbz2-1.0, libc6, libfontenc1, libfreetype6, libgcc1, libgl1-mesa-dri, libgnutls28, libjpeg-turbo8, libp11-kit0, libpam0g, libpixman-1-0, libstdc++6, libtasn1-bin, libx11-6, libxau6, libxcb1, libxdmcp6, libxext6, libxfont1, libxtst6, zlib1g, libglu1-mesa, libxcursor1, libxinerama1, libxfixes3, x11-xkb-utils, libgcrypt20
 Recommends: xfonts-base, x11-xserver-utils
 Suggests: xtigervncviewer, tigervnc-java
 Description: virtual network computing server software
@@ -30,7 +30,7 @@
 Package: xtigervncviewer
 Architecture: any
 Provides: vncviewer, vnc-viewer
-Depends: libc6, libexpat1, libfontconfig1, libfreetype6, libgcc1, libgnutls28, libjpeg-turbo8, libp11-kit0, libpng12-0, libstdc++6, libtasn1-3-bin, libx11-6, libxau6, libxcb1, libxdmcp6, libxext6, libxft2, libxrender1, zlib1g, libglu1-mesa, libxcursor1, libxinerama1, libxfixes3
+Depends: libc6, libexpat1, libfontconfig1, libfreetype6, libgcc1, libgnutls28, libjpeg-turbo8, libp11-kit0, libpng12-0, libstdc++6, libtasn1-bin, libx11-6, libxau6, libxcb1, libxdmcp6, libxext6, libxft2, libxrender1, zlib1g, libglu1-mesa, libxcursor1, libxinerama1, libxfixes3
 Recommends: xfonts-base
 Suggests: tigervncserver, ssh
 Description: virtual network computing client software for X
diff --git a/java/CMakeLists.txt b/java/CMakeLists.txt
index 7d6c70a..82de6c0 100644
--- a/java/CMakeLists.txt
+++ b/java/CMakeLists.txt
@@ -126,15 +126,18 @@
 
 if(NOT "${SRCDIR}" STREQUAL "${BINDIR}")
 
-add_custom_command(OUTPUT ${BINDIR}/${CLASSPATH}/tigervnc.png
-  COMMAND ${CMAKE_COMMAND} -E copy_if_different
-    ${SRCDIR}/${CLASSPATH}/tigervnc.png ${BINDIR}/${CLASSPATH}/tigervnc.png
-  DEPENDS ${SRCDIR}/${CLASSPATH}/tigervnc.png)
+set(ICONS
+tigervnc.ico
+tigervnc.png
+insecure.png
+secure.png)
 
-add_custom_command(OUTPUT ${BINDIR}/${CLASSPATH}/tigervnc.ico
-  COMMAND ${CMAKE_COMMAND} -E copy_if_different
-    ${SRCDIR}/${CLASSPATH}/tigervnc.ico ${BINDIR}/${CLASSPATH}/tigervnc.ico
-  DEPENDS ${SRCDIR}/${CLASSPATH}/tigervnc.ico)
+foreach(icon ${ICONS})
+  add_custom_command(OUTPUT ${BINDIR}/${CLASSPATH}/${icon}
+    COMMAND ${CMAKE_COMMAND} -E copy_if_different
+      ${SRCDIR}/${CLASSPATH}/${icon} ${BINDIR}/${CLASSPATH}/${icon}
+    DEPENDS ${SRCDIR}/${CLASSPATH}/${icon})
+endforeach()
 
 endif()
 
@@ -145,8 +148,10 @@
   DEPENDS ${JAVA_CLASSES}
     ${SRCDIR}/${CLASSPATH}/MANIFEST.MF
     ${BINDIR}/${CLASSPATH}/timestamp
-    ${BINDIR}/${CLASSPATH}/tigervnc.png
     ${BINDIR}/${CLASSPATH}/tigervnc.ico
+    ${BINDIR}/${CLASSPATH}/tigervnc.png
+    ${BINDIR}/${CLASSPATH}/insecure.png
+    ${BINDIR}/${CLASSPATH}/secure.png
   COMMAND ${JAVA_ARCHIVE}
   ARGS cfm VncViewer.jar
     ${SRCDIR}/${CLASSPATH}/MANIFEST.MF
@@ -159,7 +164,7 @@
     com/jcraft/jsch/jcraft/*.class
     com/jcraft/jsch/jce/*.class
     com/jcraft/jsch/*.class
-    com/tigervnc/vncviewer/tigervnc.png
+    com/tigervnc/vncviewer/*.png
     com/tigervnc/vncviewer/tigervnc.ico
   COMMAND ${CMAKE_COMMAND}
   ARGS -DJava_PATH=${Java_PATH} -DJAR_FILE=${BINDIR}/VncViewer.jar
diff --git a/java/com/tigervnc/rfb/CConnection.java b/java/com/tigervnc/rfb/CConnection.java
index aefc276..c9b6b89 100644
--- a/java/com/tigervnc/rfb/CConnection.java
+++ b/java/com/tigervnc/rfb/CConnection.java
@@ -389,6 +389,8 @@
   // Identities, to determine the unique(ish) name of the server.
   public String getServerName() { return serverName; }
 
+  public boolean isSecure() { return csecurity != null ? csecurity.isSecure() : false; }
+
   public static final int RFBSTATE_UNINITIALISED = 0;
   public static final int RFBSTATE_PROTOCOL_VERSION = 1;
   public static final int RFBSTATE_SECURITY_TYPES = 2;
diff --git a/java/com/tigervnc/rfb/CSecurity.java b/java/com/tigervnc/rfb/CSecurity.java
index f67680c..f192d30 100644
--- a/java/com/tigervnc/rfb/CSecurity.java
+++ b/java/com/tigervnc/rfb/CSecurity.java
@@ -37,10 +37,11 @@
   abstract public boolean processMsg(CConnection cc);
   abstract public int getType();
   abstract public String description();
+  public boolean isSecure() { return false; }
 
   /*
    * Use variable directly instead of dumb get/set methods.
    * It MUST be set by viewer.
    */
-  static UserPasswdGetter upg;
+  public static UserPasswdGetter upg;
 }
diff --git a/java/com/tigervnc/rfb/CSecurityIdent.java b/java/com/tigervnc/rfb/CSecurityIdent.java
index 9eb6e0b..872b84a 100644
--- a/java/com/tigervnc/rfb/CSecurityIdent.java
+++ b/java/com/tigervnc/rfb/CSecurityIdent.java
@@ -1,4 +1,4 @@
-/* Copyright (C) 2011 Brian P. Hinz
+/* Copyright (C) 2011-2017 Brian P. Hinz
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -30,7 +30,7 @@
 
     StringBuffer username = new StringBuffer();
 
-    CConn.upg.getUserPasswd(username, null);
+    upg.getUserPasswd(cc.isSecure(), username, null);
 
     // Return the response to the server
     os.writeU32(username.length());
@@ -46,9 +46,6 @@
 
   public int getType() { return Security.secTypeIdent; }
 
-  java.net.Socket sock;
-  UserPasswdGetter upg;
-
   static LogWriter vlog = new LogWriter("Ident");
   public String description() { return "No Encryption"; }
 
diff --git a/java/com/tigervnc/rfb/CSecurityPlain.java b/java/com/tigervnc/rfb/CSecurityPlain.java
index d6f8ffd..ef7a10e 100644
--- a/java/com/tigervnc/rfb/CSecurityPlain.java
+++ b/java/com/tigervnc/rfb/CSecurityPlain.java
@@ -1,6 +1,6 @@
 /* Copyright (C) 2005 Martin Koegler
  * Copyright (C) 2010 TigerVNC Team
- * Copyright (C) 2011 Brian P. Hinz
+ * Copyright (C) 2011-2017 Brian P. Hinz
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -34,7 +34,7 @@
     StringBuffer username = new StringBuffer();
     StringBuffer password = new StringBuffer();
 
-    CConn.upg.getUserPasswd(username, password);
+    upg.getUserPasswd(cc.isSecure(), username, password);
 
     // Return the response to the server
     os.writeU32(username.length());
diff --git a/java/com/tigervnc/rfb/CSecurityStack.java b/java/com/tigervnc/rfb/CSecurityStack.java
index e4f5988..31e21f7 100644
--- a/java/com/tigervnc/rfb/CSecurityStack.java
+++ b/java/com/tigervnc/rfb/CSecurityStack.java
@@ -59,6 +59,15 @@
     return res;
   }
 
+  public boolean isSecure()
+  {
+    if (state0 != null && state0.isSecure())
+      return true;
+    if (state == 1 && state1 != null && state1.isSecure())
+      return true;
+    return false;
+  }
+
   public final int getType() { return type; }
   public final String description() { return name; }
 
diff --git a/java/com/tigervnc/rfb/CSecurityTLS.java b/java/com/tigervnc/rfb/CSecurityTLS.java
index 733e97d..e07ab9b 100644
--- a/java/com/tigervnc/rfb/CSecurityTLS.java
+++ b/java/com/tigervnc/rfb/CSecurityTLS.java
@@ -56,6 +56,8 @@
 import com.tigervnc.network.*;
 import com.tigervnc.vncviewer.*;
 
+import static javax.swing.JOptionPane.*;
+
 public class CSecurityTLS extends CSecurity {
 
   public static StringParameter X509CA
@@ -64,6 +66,7 @@
   public static StringParameter X509CRL
   = new StringParameter("X509CRL",
                         "X509 CRL file", "", Configuration.ConfigurationObject.ConfViewer);
+  public static UserMsgBox msg;
 
   private void initGlobal()
   {
@@ -254,6 +257,16 @@
     {
       Collection<? extends Certificate> certs = null;
       X509Certificate cert = chain[0];
+      try {
+        cert.checkValidity();
+      } catch(CertificateNotYetValidException e) {
+        throw new AuthFailureException("server certificate has not been activated");
+      } catch(CertificateExpiredException e) {
+        if (!msg.showMsgBox(YES_NO_OPTION, "certificate has expired",
+			      "The certificate of the server has expired, "+
+			      "do you want to continue?"))
+          throw new AuthFailureException("server certificate has expired");
+      }
       String thumbprint = getThumbprint(cert);
       File vncDir = new File(FileUtils.getVncHomeDir());
       File certFile = new File(vncDir, "x509_savedcerts.pem");
@@ -270,8 +283,7 @@
         tm.checkServerTrusted(chain, authType);
       } catch (java.lang.Exception e) {
         if (e.getCause() instanceof CertPathBuilderException) {
-          Object[] answer = {"YES", "NO"};
-          int ret = JOptionPane.showOptionDialog(null,
+          String certinfo =
             "This certificate has been signed by an unknown authority\n"+
             "\n"+
             "  Subject: "+cert.getSubjectX500Principal().getName()+"\n"+
@@ -283,46 +295,38 @@
             "  Not Valid After: "+cert.getNotAfter()+"\n"+
             "  SHA1 Fingerprint: "+getThumbprint(cert)+"\n"+
             "\n"+
-            "Do you want to save it and continue?",
-            "Certificate Issuer Unknown",
-            JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
-            null, answer, answer[0]);
-          if (ret == JOptionPane.YES_OPTION) {
+            "Do you want to save it and continue?";
+          if (!msg.showMsgBox(YES_NO_OPTION, "certificate issuer unknown",
+                certinfo)) {
+            throw new AuthFailureException("certificate issuer unknown");
+          }
+          if (certs == null || !certs.contains(cert)) {
+            byte[] der = cert.getEncoded();
+            String pem = DatatypeConverter.printBase64Binary(der);
+            pem = pem.replaceAll("(.{64})", "$1\n");
+            FileWriter fw = null;
             try {
               if (!vncDir.exists())
                 vncDir.mkdir();
               if (!certFile.exists() && !certFile.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;
-            }
-            if (certs == null || !certs.contains(cert)) {
-              byte[] der = cert.getEncoded();
-              String pem = DatatypeConverter.printBase64Binary(der);
-              pem = pem.replaceAll("(.{64})", "$1\n");
-              FileWriter fw = null;
-              try {
+              } else {
                 fw = new FileWriter(certFile.getAbsolutePath(), true);
                 fw.write("-----BEGIN CERTIFICATE-----\n");
                 fw.write(pem+"\n");
                 fw.write("-----END CERTIFICATE-----\n");
-              } catch (IOException ioe) {
-                throw new Exception(ioe.getMessage());
-              } finally {
-                try {
-                  if (fw != null)
-                    fw.close();
-                } catch(IOException ioe2) {
-                  throw new Exception(ioe2.getMessage());
-                }
+              }
+            } catch (IOException ioe) {
+              msg.showMsgBox(OK_OPTION, "certificate save failed",
+                             "Could not save the certificate");
+            } finally {
+              try {
+                if (fw != null)
+                  fw.close();
+              } catch(IOException ioe2) {
+                throw new Exception(ioe2.getMessage());
               }
             }
-          } else {
-            throw new WarningException("Peer certificate verification failed.");
           }
         } else {
           throw new SystemException(e.getMessage());
@@ -458,6 +462,7 @@
   public final int getType() { return anon ? Security.secTypeTLSNone : Security.secTypeX509None; }
   public final String description()
     { return anon ? "TLS Encryption without VncAuth" : "X509 Encryption without VncAuth"; }
+  public boolean isSecure() { return !anon; }
 
   protected CConnection client;
 
diff --git a/java/com/tigervnc/rfb/CSecurityVeNCrypt.java b/java/com/tigervnc/rfb/CSecurityVeNCrypt.java
index 179900a..8bffedf 100644
--- a/java/com/tigervnc/rfb/CSecurityVeNCrypt.java
+++ b/java/com/tigervnc/rfb/CSecurityVeNCrypt.java
@@ -178,7 +178,19 @@
   }
 
   public final int getType() { return chosenType; }
-  public final String description() { return Security.secTypeName(chosenType); }
+  public final String description()
+  {
+    if (csecurity != null)
+      return csecurity.description();
+    return "VeNCrypt";
+  }
+
+  public final boolean isSecure()
+  {
+    if (csecurity != null && csecurity.isSecure())
+      return true;
+    return false;
+  }
 
   public static StringParameter secTypesStr;
 
diff --git a/java/com/tigervnc/rfb/CSecurityVncAuth.java b/java/com/tigervnc/rfb/CSecurityVncAuth.java
index e053e41..0615495 100644
--- a/java/com/tigervnc/rfb/CSecurityVncAuth.java
+++ b/java/com/tigervnc/rfb/CSecurityVncAuth.java
@@ -36,7 +36,7 @@
     byte[] challenge = new byte[vncAuthChallengeSize];
     is.readBytes(challenge, 0, vncAuthChallengeSize);
     StringBuffer passwd = new StringBuffer();
-    CConn.upg.getUserPasswd(null, passwd);
+    upg.getUserPasswd(cc.isSecure(), null, passwd);
 
     // Calculate the correct response
     byte[] key = new byte[8];
diff --git a/java/com/tigervnc/rfb/SecurityClient.java b/java/com/tigervnc/rfb/SecurityClient.java
index ff2433c..d355733 100644
--- a/java/com/tigervnc/rfb/SecurityClient.java
+++ b/java/com/tigervnc/rfb/SecurityClient.java
@@ -1,6 +1,6 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
  * Copyright (C) 2010 TigerVNC Team
- * Copyright (C) 2011-2012 Brian P. Hinz
+ * Copyright (C) 2011-2017 Brian P. Hinz
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,16 +20,14 @@
 
 package com.tigervnc.rfb;
 
-import com.tigervnc.vncviewer.CConn;
-
 public class SecurityClient extends Security {
 
   public SecurityClient() { super(secTypes); }
 
   public CSecurity GetCSecurity(int secType)
   {
-    assert (CConn.upg != null); /* (upg == null) means bug in the viewer */
-    assert (msg != null);
+    assert (CSecurity.upg != null); /* (upg == null) means bug in the viewer */
+    assert (CSecurityTLS.msg != null);
 
     if (!IsSupported(secType))
       throw new Exception("Security type not supported");
@@ -75,9 +73,6 @@
       CSecurityTLS.setDefaults();
   }
 
-  //UserPasswdGetter upg = null;
-  String msg = null;
-
   public static StringParameter secTypes
   = new StringParameter("SecurityTypes",
                         "Specify which security scheme to use (None, VncAuth, Plain, Ident, TLSNone, TLSVnc, TLSPlain, TLSIdent, X509None, X509Vnc, X509Plain, X509Ident)",
diff --git a/java/com/tigervnc/rfb/UserPasswdGetter.java b/java/com/tigervnc/rfb/UserPasswdGetter.java
index feb05ed..457eaf2 100644
--- a/java/com/tigervnc/rfb/UserPasswdGetter.java
+++ b/java/com/tigervnc/rfb/UserPasswdGetter.java
@@ -23,5 +23,5 @@
 package com.tigervnc.rfb;
 
 public interface UserPasswdGetter {
-  public boolean getUserPasswd(StringBuffer user, StringBuffer password);
+  public void getUserPasswd(boolean secure, StringBuffer user, StringBuffer password);
 }
diff --git a/java/com/tigervnc/vncviewer/CConn.java b/java/com/tigervnc/vncviewer/CConn.java
index d71c307..3aee46d 100644
--- a/java/com/tigervnc/vncviewer/CConn.java
+++ b/java/com/tigervnc/vncviewer/CConn.java
@@ -63,7 +63,7 @@
 import static com.tigervnc.vncviewer.Parameters.*;
 
 public class CConn extends CConnection implements 
-  UserPasswdGetter, FdInStreamBlockCallback, ActionListener {
+  FdInStreamBlockCallback, ActionListener {
 
   // 8 colours (1 bit per component)
   static final PixelFormat verylowColorPF =
@@ -92,8 +92,6 @@
     setShared(shared.getValue());
     sock = socket;
 
-    upg = this;
-
     int encNum = Encodings.encodingNum(preferredEncoding.getValue());
     if (encNum != -1)
       currentEncoding = encNum;
@@ -208,58 +206,6 @@
     }
   }
 
-  // getUserPasswd() is called by the CSecurity object when it needs us to read
-  // a password from the user.
-
-  public final boolean getUserPasswd(StringBuffer user, StringBuffer passwd) {
-    String title = ("VNC Authentication ["
-                    +csecurity.description() + "]");
-    String passwordFileStr = passwordFile.getValue();
-    PasswdDialog dlg;
-
-    if (user == null && !passwordFileStr.equals("")) {
-      InputStream fp = null;
-      try {
-        fp = new FileInputStream(passwordFileStr);
-      } catch(FileNotFoundException e) {
-        throw new Exception("Opening password file failed");
-      }
-      byte[] obfPwd = new byte[256];
-      try {
-        fp.read(obfPwd);
-        fp.close();
-      } catch(IOException e) {
-        throw new Exception("Failed to read VncPasswd file");
-      }
-      String PlainPasswd = VncAuth.unobfuscatePasswd(obfPwd);
-      passwd.append(PlainPasswd);
-      passwd.setLength(PlainPasswd.length());
-      return true;
-    }
-
-    if (user == null) {
-      dlg = new PasswdDialog(title, (user == null), (passwd == null));
-    } else {
-      if ((passwd == null) && sendLocalUsername.getValue()) {
-         user.append((String)System.getProperties().get("user.name"));
-         return true;
-      }
-      dlg = new PasswdDialog(title, sendLocalUsername.getValue(),
-         (passwd == null));
-    }
-    dlg.showDialog();
-    if (user != null) {
-      if (sendLocalUsername.getValue()) {
-         user.append((String)System.getProperties().get("user.name"));
-      } else {
-         user.append(dlg.userEntry.getText());
-      }
-    }
-    if (passwd != null)
-      passwd.append(new String(dlg.passwdEntry.getPassword()));
-    return true;
-  }
-
   ////////////////////// CConnection callback methods //////////////////////
 
   // serverInit() is called when the serverInit message has been received.  At
@@ -729,8 +675,6 @@
 
   // the following need no synchronization:
 
-  public static UserPasswdGetter upg;
-
   // shuttingDown is set by the GUI thread and only ever tested by the RFB
   // thread after the window has been destroyed.
   boolean shuttingDown = false;
diff --git a/java/com/tigervnc/vncviewer/UserDialog.java b/java/com/tigervnc/vncviewer/UserDialog.java
new file mode 100644
index 0000000..dfced98
--- /dev/null
+++ b/java/com/tigervnc/vncviewer/UserDialog.java
@@ -0,0 +1,208 @@
+/* Copyright (C) 2017 Brian P. Hinz
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+package com.tigervnc.vncviewer;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.border.*;
+import javax.swing.plaf.LayerUI;
+
+import com.tigervnc.rfb.*;
+import com.tigervnc.rfb.Point;
+import com.tigervnc.rfb.Exception;
+
+import static com.tigervnc.vncviewer.Parameters.*;
+import static javax.swing.GroupLayout.*;
+import static javax.swing.JOptionPane.*;
+
+
+public class UserDialog implements UserPasswdGetter, UserMsgBox
+{
+  private class MyLayerUI extends LayerUI {
+    // Using a JButton for the "?" icon yields the best look, but there
+    // does not seem to be any reasonable way to disable a JButton without
+    // also changing the color.  This wrapper just intercepts any mouse
+    // click events so that the button just looks like an icon.
+    @Override
+    public void eventDispatched(AWTEvent e, JLayer l) {
+      if (e instanceof InputEvent)
+        ((InputEvent) e).consume();
+    }
+
+    @Override
+    public void installUI(JComponent c) {
+      super.installUI(c);
+      if (c instanceof JLayer) {
+        JLayer<?> layer = (JLayer<?>)c;
+        layer.setLayerEventMask(AWTEvent.MOUSE_EVENT_MASK);
+      }
+    }
+
+    @Override
+    protected void processMouseEvent(MouseEvent e, JLayer l) {
+      super.processMouseEvent(e, l);
+    }
+  }
+
+  public final void getUserPasswd(boolean secure, StringBuffer user, StringBuffer password)
+  {
+    String passwordFileStr = passwordFile.getValue();
+
+    if ((password == null) && sendLocalUsername.getValue()) {
+      user.append((String)System.getProperties().get("user.name"));
+      return;
+    }
+
+    if (user == null && !passwordFileStr.equals("")) {
+      InputStream fp = null;
+      try {
+        fp = new FileInputStream(passwordFileStr);
+      } catch(FileNotFoundException e) {
+        throw new Exception("Opening password file failed");
+      }
+      byte[] obfPwd = new byte[256];
+      try {
+        fp.read(obfPwd);
+        fp.close();
+      } catch(IOException e) {
+        throw new Exception("Failed to read VncPasswd file");
+      }
+      String PlainPasswd = VncAuth.unobfuscatePasswd(obfPwd);
+      password.append(PlainPasswd);
+      password.setLength(PlainPasswd.length());
+      return;
+    }
+
+    JDialog win;
+    JLabel banner;
+    JTextField username = null;
+    JPasswordField passwd = null;
+    JLayer icon;
+
+    int y;
+
+    JPanel msg = new JPanel(null);
+    msg.setSize(410, 145);
+
+    banner = new JLabel();
+    banner.setBounds(0, 0, msg.getPreferredSize().width, 20);
+    banner.setHorizontalAlignment(JLabel.CENTER);
+    banner.setOpaque(true);
+
+    if (secure) {
+      banner.setText("This connection is secure");
+      banner.setBackground(Color.GREEN);
+      ImageIcon secure_icon =
+        new ImageIcon(VncViewer.class.getResource("secure.png"));
+      banner.setIcon(secure_icon);
+    } else {
+      banner.setText("This connection is not secure");
+      banner.setBackground(Color.RED);
+      ImageIcon insecure_icon =
+        new ImageIcon(VncViewer.class.getResource("insecure.png"));
+      banner.setIcon(insecure_icon);
+    }
+    msg.add(banner);
+
+    y = 20 + 10;
+
+    JButton iconb = new JButton("?");
+    iconb.setVerticalAlignment(JLabel.CENTER);
+    iconb.setFont(new Font("Times", Font.BOLD, 34));
+    iconb.setForeground(Color.BLUE);
+    LayerUI ui = new MyLayerUI();
+    icon = new JLayer(iconb, ui);
+    icon.setBounds(10, y, 50, 50);
+    msg.add(icon);
+
+    y += 5;
+
+    if (user != null && !sendLocalUsername.getValue()) {
+      JLabel userLabel = new JLabel("Username:");
+      userLabel.setBounds(70, y, msg.getSize().width-70-10, 20);
+      msg.add(userLabel);
+      y += 20 + 5;
+      username = new JTextField(30);
+      username.setBounds(70, y, msg.getSize().width-70-10, 25);
+      msg.add(username);
+      y += 25 + 5;
+    }
+
+    JLabel passwdLabel = new JLabel("Password:");
+    passwdLabel.setBounds(70, y, msg.getSize().width-70-10, 20);
+    msg.add(passwdLabel);
+    y += 20 + 5;
+    passwd = new JPasswordField(30);
+    passwd.setBounds(70, y, msg.getSize().width-70-10, 25);
+    msg.add(passwd);
+    y += 25 + 5;
+
+    msg.setPreferredSize(new Dimension(410, y));
+
+    Object[] options = {"OK  \u21B5", "Cancel"};
+    JOptionPane pane = new JOptionPane(msg,
+                                      PLAIN_MESSAGE,
+                                      OK_CANCEL_OPTION,
+                                      null,       //do not use a custom Icon
+                                      options,    //the titles of buttons
+                                      options[0]);//default button title
+    pane.setBorder(new EmptyBorder(0,0,0,0));
+    Component c = pane.getComponent(pane.getComponentCount()-1);
+    ((JComponent)c).setBorder(new EmptyBorder(0,0,10,10));
+    win = pane.createDialog("VNC Authentication");
+
+    win.setVisible(true);
+
+    if (pane.getValue() == null || pane.getValue().equals("Cancel"))
+      throw new Exception("Authentication cancelled");
+
+    if (user != null)
+      if (sendLocalUsername.getValue())
+         user.append((String)System.getProperties().get("user.name"));
+      else
+         user.append(username.getText());
+    if (password != null)
+      password.append(new String(passwd.getPassword()));
+  }
+
+  public boolean showMsgBox(int flags, String title, String text)
+  {
+    switch (flags & 0xf) {
+    case OK_CANCEL_OPTION:
+      return (showConfirmDialog(null, text, title, OK_CANCEL_OPTION) == OK_OPTION);
+    case YES_NO_OPTION:
+      return (showConfirmDialog(null, text, title, YES_NO_OPTION) == YES_OPTION);
+    default:
+      if (((flags & 0xf0) == ERROR_MESSAGE) ||
+          ((flags & 0xf0) == WARNING_MESSAGE))
+        showMessageDialog(null, text, title, (flags & 0xf0));
+      else
+        showMessageDialog(null, text, title, PLAIN_MESSAGE);
+      return true;
+    }
+  }
+}
diff --git a/java/com/tigervnc/vncviewer/VncViewer.java b/java/com/tigervnc/vncviewer/VncViewer.java
index 8786c11..74f2ca3 100644
--- a/java/com/tigervnc/vncviewer/VncViewer.java
+++ b/java/com/tigervnc/vncviewer/VncViewer.java
@@ -486,6 +486,9 @@
 
   public void run() {
     cc = null;
+    UserDialog dlg = new UserDialog();
+    CSecurity.upg = dlg;
+    CSecurityTLS.msg = dlg;
     Socket sock = null;
 
     /* Specifying -via and -listen together is nonsense */
diff --git a/java/com/tigervnc/vncviewer/insecure.png b/java/com/tigervnc/vncviewer/insecure.png
new file mode 100644
index 0000000..f48faff
--- /dev/null
+++ b/java/com/tigervnc/vncviewer/insecure.png
Binary files differ
diff --git a/java/com/tigervnc/vncviewer/secure.png b/java/com/tigervnc/vncviewer/secure.png
new file mode 100644
index 0000000..9383371
--- /dev/null
+++ b/java/com/tigervnc/vncviewer/secure.png
Binary files differ
diff --git a/media/insecure.xpm b/media/insecure.xpm
index f5053fe..e42ee14 100644
--- a/media/insecure.xpm
+++ b/media/insecure.xpm
@@ -1,71 +1,44 @@
 /* XPM */
 static char *insecure[] = {
 /* columns rows colors chars-per-pixel */
-"15 15 50 1 ",
-"  c black",
-". c #020000",
-"X c #050000",
-"o c #080000",
-"O c #0A0000",
-"+ c #0C0000",
-"@ c #0D0000",
-"# c #0F0000",
-"$ c #100000",
-"% c #110000",
-"& c #120000",
-"* c #140000",
-"= c #290000",
-"- c #330000",
-"; c #370000",
-": c #430000",
-"> c #560000",
-", c #620000",
-"< c #660000",
-"1 c #6C0000",
-"2 c #7D0000",
-"3 c #800000",
-"4 c #810000",
-"5 c #840000",
-"6 c #870000",
-"7 c #950000",
-"8 c #A20000",
-"9 c #AB0000",
-"0 c #B30000",
-"q c #B40000",
-"w c #C00000",
-"e c #C40000",
-"r c #CD0000",
-"t c #DC0000",
-"y c #DD0000",
-"u c #DF0000",
-"i c #E40000",
-"p c #E50000",
-"a c #E60000",
-"s c #EA0000",
-"d c #EB0000",
-"f c #ED0000",
-"g c #F00000",
-"h c #F40000",
-"j c #F90000",
-"k c #FA0000",
-"l c #FB0000",
-"z c #FC0000",
-"x c #FD0000",
-"c c red",
+"16 16 22 1 ",
+"  c #000000",
+". c #040000",
+"X c #0D0000",
+"o c #120000",
+"O c #3A0000",
+"+ c #3D0000",
+"@ c #4B0000",
+"# c #530000",
+"$ c #5B0000",
+"% c #770000",
+"& c #810000",
+"* c #8E0000",
+"= c #940000",
+"- c #980000",
+"; c #AB0000",
+": c #BB0000",
+"> c #C00000",
+", c #DC0000",
+"< c #E30000",
+"1 c #E40000",
+"2 c #FC0000",
+"3 c #FF0000",
 /* pixels */
-"ccccccccccccccc",
-"ccccccjpkcr:fcc",
-"ccccz6+ @1$ rcc",
-"cccc2 O-o  wccc",
-"cccdX%tr# 4cccc",
-"ccce >r& 7czccc",
-"ccq< =O 8cg60cc",
-"cs.    3cg;  ac",
-"cu    3cg;   uc",
-"cu   3cg;    uc",
-"c0  3cg;     uc",
-"r$ 3cg;      uc",
-", 5cg;      *hc",
-"l9lxiyyyyyyyhcc",
-"ccccccccccccccc"
+"3333333333333333",
+"33333333333;%333",
+"33333<#XX#*. ,33",
+"3333<o      :333",
+"3333# @1-  =3333",
+"3333X <;. %33333",
+"3333  ;. :333333",
+"33O     &33$ O33",
+"33     %33$   33",
+"33    &33$    33",
+"33   &33$     33",
+"3;  &33$      33",
+";. &33$       33",
+"& *33$       +33",
+"3>33333333333333",
+"3333333333333333"
 };
diff --git a/media/secure.xpm b/media/secure.xpm
index 49a3791..8d89548 100644
--- a/media/secure.xpm
+++ b/media/secure.xpm
@@ -1,56 +1,34 @@
 /* XPM */
 static char *secure[] = {
 /* columns rows colors chars-per-pixel */
-"15 15 35 1 ",
-"  c black",
-". c #000200",
-"X c #000500",
-"o c #000A00",
-"O c #000C00",
-"+ c #000D00",
-"@ c #001000",
-"# c #001100",
-"$ c #001400",
-"% c #003300",
-"& c #005500",
-"* c #005600",
-"= c #006600",
-"- c #007D00",
-"; c #007E00",
-": c #008700",
-"> c #008800",
-", c #00B300",
-"< c #00B400",
-"1 c #00C400",
-"2 c #00DB00",
-"3 c #00DC00",
-"4 c #00DD00",
-"5 c #00DF00",
-"6 c #00E500",
-"7 c #00E600",
-"8 c #00EA00",
-"9 c #00EB00",
-"0 c #00ED00",
-"q c #00F000",
-"w c #00F400",
-"e c #00F900",
-"r c #00FA00",
-"t c #00FC00",
-"y c green",
+"16 16 12 1 ",
+"  c #000000",
+". c #000D00",
+"X c #001200",
+"o c #003A00",
+"O c #003C00",
+"+ c #004B00",
+"@ c #005300",
+"# c #005500",
+"$ c #00D800",
+"% c #00E300",
+"& c #00E400",
+"* c #00FF00",
 /* pixels */
-"yyyyyyyyyyyyyyy",
-"yyyyyye6ryyyyyy",
-"yyyyt:O +>tyyyy",
-"yyyy- o%o ;yyyy",
-"yyy9X#3y2@X0yyy",
-"yyy1 *yyy& 1yyy",
-"yy<= %>>>% =,yy",
-"y8.          7y",
-"y5           5y",
-"y5           5y",
-"y5           5y",
-"y5           5y",
-"yw#         $wy",
-"yyq444444444wyy",
-"yyyyyyyyyyyyyyy"
+"****************",
+"****************",
+"*****%@..@$*****",
+"****%X    .%****",
+"****@ +&%+ #****",
+"****. %**% .****",
+"****  ****  ****",
+"**o          O**",
+"**            **",
+"**            **",
+"**            **",
+"**            **",
+"**            **",
+"**O          O**",
+"****************",
+"****************"
 };