Commit 97540de5 authored by runge's avatar runge

classes/ssl: Many improvements to Java SSL applet, onetimekey

          serverCert param, debugging printout, user dialogs, catch
          socket exceptions, autodetect x11vnc for GET=1.
x11vnc: misc/scripts: desktop.cgi, inet6to4, panner.pl.
          X11VNC_HTTPS_DOWNLOAD_WAIT_TIME, -unixpw %xxx documented, and
          can run user cmd in UNIXPW_CMD. FD_XDMCP_IF for create script,
          autodetect dm on udp6 only.  Queries: pointer_x, pointer_y,
          pointer_same, pointer_root.  Switch on -xkd if keysyms per key >
          4 in all cases.  daemon mode improvements for connect_switch,
          inet6to4, ultravnc_repeater.pl.  Dynamic change of -clip do
          not create new fb if WxH is unchanged.
parent edb79ae2
......@@ -137,6 +137,15 @@ Both TightVNC and UltraVNC Java viewers:
number, default: 50
Milliseconds delay
PASSWORD
string, default: none
VNC session password in plain text.
ENCPASSWORD
string, default: none
VNC session password in encrypted in DES with KNOWN FIXED
key. It is a hex string. This is like the ~/.vnc/passwd format.
The following are added by x11vnc and/or ssvnc project
......@@ -173,16 +182,47 @@ Both TightVNC and UltraVNC Java viewers:
oneTimeKey
string, default: none
set a special hex "key" to correspond to an SSL X.509 cert.
See the 'onetimekey' helper script. Can also be PROMPT to
prompt the user to paste the hex key string in.
set a special hex "key" to correspond to an SSL X.509 cert+key.
See the 'onetimekey' helper script. Can also be PROMPT to prompt
the user to paste the hex key string in.
This provides a Client-Side cert+key that the client will use to
authenticate itself by SSL To the VNC Server.
This is to try to work around the problem that the Java applet
cannot keep an SSL keystore on disk, etc. E.g. if they log
into an HTTPS website via password they are authenticated and
encrypted, then the website can safely put oneTimeKey=... on the
URL. The Vncviewer authenticates the VNC server with this key.
Note that there is currently a problem in that if x11vnc requires
Client Certificates the user cannot download the index.vnc HTML
and VncViewer.jar from the same x11vnc. Those need to come from
a different x11vnc or from a web server.
Note that the HTTPS website can also put the VNC Password
(e.g. a temporary/one-time one) in the parameter PASSWORD.
The Java Applet will automatically supply this VNC password
instead of prompting.
serverCert
string, default: none
set a special hex "cert" to correspond to an SSL X.509 cert
See the 'onetimekey -certonly' helper script.
This is to try to work around the problem that the Java
applet cannot keep an SSL keystore on disk, etc.
E.g. if they log into an HTTPS website via password they
are authenticated and encrypted, then the website can
safely put oneTimeKey=... on the URL. The Vncviewer
authenticates the VNC server with this key.
This provides a Server-Side cert that the client will authenticate
the VNC Server against by SSL.
This is to try to work around the problem that the Java applet
cannot keep an SSL keystore on disk, etc. E.g. if they log
into an HTTPS website via password they are authenticated and
encrypted, then the website can safely put serverCert=... on the
URL.
Of course the VNC Server is sending this string to the Java
Applet, so this is only reasonable security if the VNC Viewer
already trusts the HTTPS retrieval of the URL + serverCert param
that it gets. This should be done over HTTPS not HTTP.
proxyHost
string, default: none
......@@ -238,15 +278,8 @@ TightVNC Java viewer only:
UltraVNC Java viewer only:
PASSWORD
string, default: none
VNC session password in plain text.
None.
ENCPASSWORD
string, default: none
VNC session password in encrypted in DES with KNOWN FIXED
key. It is a hex string. This is like the ~/.vnc/passwd format.
The following are added by x11vnc and/or ssvnc project
ftpDropDown
......
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
#!/bin/sh
#
# usage: onetimekey path/to/mycert.pem
# onetimekey -certonly path/to/mycert.pem
#
# Takes an openssl cert+key pem file and turns into a long string
# for the x11vnc SSL VNC Java Viewer.
......@@ -14,6 +15,19 @@
# in it. Also, as the name implies, an HTTPS server can create
# a one time key to send to the applet (the user has already
# logged in via password to the HTTPS server).
#
# Note oneTimeKey is to provide a CLIENT Certificate for the viewer
# to authenticate itself to the VNC Server.
#
# There is also the serverCert=<str> Applet parameter. This is
# a cert to authenticate the VNC server against. To create that
# string with this tool specify -certonly as the first argument.
certonly=""
if [ "X$1" = "X-certonly" ]; then
shift
certonly=1
fi
in=$1
der=/tmp/1time$$.der
......@@ -43,5 +57,9 @@ rm -f "$der"
n=`grep -n 'BEGIN CERTIFICATE' $in | awk -F: '{print $1}' | head -1`
str2=`tail +$n $in | $pbinhex`
echo "$str1,$str2"
if [ "X$certonly" = "X1" ]; then
echo "$str2"
else
echo "$str1,$str2"
fi
rm -f $pbinhex
......@@ -73,8 +73,8 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/RfbProto.java vnc_javasrc/RfbProto
serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0');
diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSLSocketToMe.java
--- vnc_javasrc.orig/SSLSocketToMe.java 1969-12-31 19:00:00.000000000 -0500
+++ vnc_javasrc/SSLSocketToMe.java 2010-02-22 20:03:11.000000000 -0500
@@ -0,0 +1,1712 @@
+++ vnc_javasrc/SSLSocketToMe.java 2010-03-19 12:52:08.000000000 -0400
@@ -0,0 +1,2055 @@
+/*
+ * SSLSocketToMe.java: add SSL encryption to Java VNC Viewer.
+ *
......@@ -118,7 +118,9 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ String host;
+ int port;
+ VncViewer viewer;
+
+ boolean debug = true;
+ boolean debug_certs = false;
+
+ /* sockets */
+ SSLSocket socket = null;
......@@ -126,11 +128,11 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+
+ /* fallback for Proxy connection */
+ boolean proxy_in_use = false;
+ boolean proxy_is_https = false;
+ boolean proxy_failure = false;
+ public DataInputStream is = null;
+ public OutputStream os = null;
+
+ /* strings from user WRT proxy: */
+ String proxy_auth_string = null;
+ String proxy_dialog_host = null;
+ int proxy_dialog_port = 0;
......@@ -142,21 +144,28 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ /* trust contexts */
+ SSLContext trustloc_ctx;
+ SSLContext trustall_ctx;
+ SSLContext trustsrv_ctx;
+ SSLContext trusturl_ctx;
+ SSLContext trustone_ctx;
+
+ /* corresponding trust managers */
+ TrustManager[] trustAllCerts;
+ TrustManager[] trustSrvCert;
+ TrustManager[] trustUrlCert;
+ TrustManager[] trustOneCert;
+
+ boolean use_url_cert_for_auth = true;
+ /* client-side SSL auth key (oneTimeKey=...) */
+ KeyManager[] mykey = null;
+
+ boolean user_wants_to_see_cert = true;
+ boolean debug_certs = false;
+ String cert_fail = null;
+
+ /* cert(s) we retrieve from VNC server */
+ /* cert(s) we retrieve from Web server, VNC server, or serverCert param: */
+ java.security.cert.Certificate[] trustallCerts = null;
+ java.security.cert.Certificate[] trustsrvCerts = null;
+ java.security.cert.Certificate[] trusturlCerts = null;
+
+ /* utility to decode hex oneTimeKey=... and serverCert=... */
+ byte[] hex2bytes(String s) {
+ byte[] bytes = new byte[s.length()/2];
+ for (int i=0; i<s.length()/2; i++) {
......@@ -189,11 +198,15 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+
+ dbg("SSL startup: " + host + " " + port);
+
+ /* create trust managers used if initial handshake fails: */
+
+ /* create trust managers to be used if initial handshake fails: */
+
+ trustAllCerts = new TrustManager[] {
+ /*
+ * this one accepts everything.
+ * this one accepts everything. Only used if user
+ * has disabled checking (trustAllVncCerts=yes)
+ * or when we grab the cert to show it to them in
+ * a dialog and ask them to manually verify/accept it.
+ */
+ new X509TrustManager() {
+ public java.security.cert.X509Certificate[]
......@@ -216,8 +229,9 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+
+ trustUrlCert = new TrustManager[] {
+ /*
+ * this one accepts only the retrieved server cert
+ * by SSLSocket by this applet.
+ * this one accepts only the retrieved server
+ * cert by SSLSocket by this applet and stored in
+ * trusturlCerts.
+ */
+ new X509TrustManager() {
+ public java.security.cert.X509Certificate[]
......@@ -227,11 +241,12 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ public void checkClientTrusted(
+ java.security.cert.X509Certificate[] certs,
+ String authType) throws CertificateException {
+ throw new CertificateException("No Clients");
+ throw new CertificateException("No Clients (URL)");
+ }
+ public void checkServerTrusted(
+ java.security.cert.X509Certificate[] certs,
+ String authType) throws CertificateException {
+ /* we want to check 'certs' against 'trusturlCerts' */
+ if (trusturlCerts == null) {
+ throw new CertificateException(
+ "No Trust url Certs array.");
......@@ -259,6 +274,9 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ dbg("URL: cert mismatch at i=" + i);
+ dbg("URL: cert mismatch cert" + certs[i]);
+ dbg("URL: cert mismatch url" + trusturlCerts[i]);
+ if (cert_fail == null) {
+ cert_fail = "cert-mismatch";
+ }
+ }
+ if (debug_certs) {
+ dbg("\n***********************************************");
......@@ -277,10 +295,86 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ }
+ }
+ };
+
+ trustSrvCert = new TrustManager[] {
+ /*
+ * this one accepts cert given to us in the serverCert
+ * Applet Parameter we were started with. It is
+ * currently a fatal error if the VNC Server's cert
+ * doesn't match it.
+ */
+ new X509TrustManager() {
+ public java.security.cert.X509Certificate[]
+ getAcceptedIssuers() {
+ return null;
+ }
+ public void checkClientTrusted(
+ java.security.cert.X509Certificate[] certs,
+ String authType) throws CertificateException {
+ throw new CertificateException("No Clients (SRV)");
+ }
+ public void checkServerTrusted(
+ java.security.cert.X509Certificate[] certs,
+ String authType) throws CertificateException {
+ /* we want to check 'certs' against 'trustsrvCerts' */
+ if (trustsrvCerts == null) {
+ throw new CertificateException(
+ "No Trust srv Certs array.");
+ }
+ if (trustsrvCerts.length < 1) {
+ throw new CertificateException(
+ "No Trust srv Certs.");
+ }
+ if (certs == null) {
+ throw new CertificateException(
+ "No this-certs array.");
+ }
+ if (certs.length < 1) {
+ throw new CertificateException(
+ "No this-certs Certs.");
+ }
+ if (certs.length != trustsrvCerts.length) {
+ throw new CertificateException(
+ "certs.length != trustsrvCerts.length " + certs.length + " " + trustsrvCerts.length);
+ }
+ boolean ok = true;
+ for (int i = 0; i < certs.length; i++) {
+ if (! trustsrvCerts[i].equals(certs[i])) {
+ ok = false;
+ dbg("SRV: cert mismatch at i=" + i);
+ dbg("SRV: cert mismatch cert" + certs[i]);
+ dbg("SRV: cert mismatch srv" + trustsrvCerts[i]);
+ if (cert_fail == null) {
+ cert_fail = "server-cert-mismatch";
+ }
+ }
+ if (debug_certs) {
+ dbg("\n***********************************************");
+ dbg("SRV: cert info at i=" + i);
+ dbg("SRV: cert info cert" + certs[i]);
+ dbg("===============================================");
+ dbg("SRV: cert info srv" + trustsrvCerts[i]);
+ dbg("***********************************************");
+ }
+ }
+ if (!ok) {
+ throw new CertificateException(
+ "Server Cert Chain != serverCert Applet Parameter Cert Chain.");
+ }
+ dbg("SRV: trustsrvCerts[i] matches certs[i] i=0:" + (certs.length-1));
+ }
+ }
+ };
+
+ trustOneCert = new TrustManager[] {
+ /*
+ * this one accepts only the retrieved server cert
+ * by SSLSocket by this applet.
+ * this one accepts only the retrieved server
+ * cert by SSLSocket by this applet we stored in
+ * trustallCerts that user has accepted or applet
+ * parameter trustAllVncCerts=yes is set. This is
+ * for when we reconnect after the user has manually
+ * accepted the trustall cert in the dialog (or set
+ * trustAllVncCerts=yes applet param.)
+ */
+ new X509TrustManager() {
+ public java.security.cert.X509Certificate[]
......@@ -290,11 +384,12 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ public void checkClientTrusted(
+ java.security.cert.X509Certificate[] certs,
+ String authType) throws CertificateException {
+ throw new CertificateException("No Clients");
+ throw new CertificateException("No Clients (ONE)");
+ }
+ public void checkServerTrusted(
+ java.security.cert.X509Certificate[] certs,
+ String authType) throws CertificateException {
+ /* we want to check 'certs' against 'trustallCerts' */
+ if (trustallCerts == null) {
+ throw new CertificateException(
+ "No Trust All Server Certs array.");
......@@ -342,20 +437,20 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ };
+
+ /*
+ * They are used:
+ * The above TrustManagers are used:
+ *
+ * 1) to retrieve the server cert in case of failure to
+ * display it to the user.
+ * display it to the user in a dialog.
+ * 2) to subsequently connect to the server if user agrees.
+ */
+
+ KeyManager[] mykey = null;
+
+ /*
+ * build oneTimeKey cert+key if supplied in applet parameter:
+ */
+ if (viewer.oneTimeKey != null && viewer.oneTimeKey.equals("PROMPT")) {
+ ClientCertDialog d = new ClientCertDialog();
+ viewer.oneTimeKey = d.queryUser();
+ }
+
+ if (viewer.oneTimeKey != null && viewer.oneTimeKey.indexOf(",") > 0) {
+ int idx = viewer.oneTimeKey.indexOf(",");
+
......@@ -367,15 +462,18 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec ( key );
+ PrivateKey ff = kf.generatePrivate (keysp);
+ //dbg("ff " + ff);
+ String cert_str = new String(cert);
+ if (debug_certs) {
+ dbg("one time key " + ff);
+ }
+
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ Collection c = cf.generateCertificates(new ByteArrayInputStream(cert));
+ Certificate[] certs = new Certificate[c.toArray().length];
+ if (c.size() == 1) {
+ Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert));
+ //dbg("tmpcert" + tmpcert);
+ if (debug_certs) {
+ dbg("one time cert" + tmpcert);
+ }
+ certs[0] = tmpcert;
+ } else {
+ certs = (Certificate[]) c.toArray();
......@@ -391,12 +489,54 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ mykey = kmf.getKeyManagers();
+ }
+
+ /*
+ * build serverCert cert if supplied in applet parameter:
+ */
+ if (viewer.serverCert != null) {
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ byte[] cert = hex2bytes(viewer.serverCert);
+ Collection c = cf.generateCertificates(new ByteArrayInputStream(cert));
+ trustsrvCerts = new Certificate[c.toArray().length];
+ if (c.size() == 1) {
+ Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert));
+ trustsrvCerts[0] = tmpcert;
+ } else {
+ trustsrvCerts = (Certificate[]) c.toArray();
+ }
+ }
+
+ /* trust loc certs: */
+ /* the trust loc certs context: */
+ try {
+ trustloc_ctx = SSLContext.getInstance("SSL");
+ trustloc_ctx.init(mykey, null, new
+ java.security.SecureRandom());
+
+ /*
+ * below is a failed attempt to get jvm's default
+ * trust manager using null (below) makes it so
+ * for HttpsURLConnection the server cannot be
+ * verified (no prompting.)
+ */
+ if (false) {
+ boolean didit = false;
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ tmf.init((KeyStore) null);
+ TrustManager [] tml = tmf.getTrustManagers();
+ for (int i = 0; i < tml.length; i++) {
+ TrustManager tm = tml[i];
+ if (tm instanceof X509TrustManager) {
+ TrustManager tm1[] = new TrustManager[1];
+ tm1[0] = tm;
+ trustloc_ctx.init(mykey, tm1, null);
+ didit = true;
+ break;
+ }
+ }
+ if (!didit) {
+ trustloc_ctx.init(mykey, null, null);
+ }
+ } else {
+ /* we have to set trust manager to null */
+ trustloc_ctx.init(mykey, null, null);
+ }
+
+ } catch (Exception e) {
+ String msg = "SSL trustloc_ctx FAILED.";
......@@ -404,7 +544,7 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ throw new Exception(msg);
+ }
+
+ /* trust all certs: */
+ /* the trust all certs context: */
+ try {
+ trustall_ctx = SSLContext.getInstance("SSL");
+ trustall_ctx.init(mykey, trustAllCerts, new
......@@ -416,7 +556,7 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ throw new Exception(msg);
+ }
+
+ /* trust url certs: */
+ /* the trust url certs context: */
+ try {
+ trusturl_ctx = SSLContext.getInstance("SSL");
+ trusturl_ctx.init(mykey, trustUrlCert, new
......@@ -428,99 +568,138 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ throw new Exception(msg);
+ }
+
+ /* trust the one cert from server: */
+ /* the trust srv certs context: */
+ try {
+ trustone_ctx = SSLContext.getInstance("SSL");
+ trustone_ctx.init(mykey, trustOneCert, new
+ trustsrv_ctx = SSLContext.getInstance("SSL");
+ trustsrv_ctx.init(mykey, trustSrvCert, new
+ java.security.SecureRandom());
+
+ } catch (Exception e) {
+ String msg = "SSL trustone_ctx FAILED.";
+ String msg = "SSL trustsrv_ctx FAILED.";
+ dbg(msg);
+ throw new Exception(msg);
+ }
+ }
+
+ boolean browser_cert_match() {
+ String msg = "Browser URL accept previously accepted cert";
+
+ if (user_wants_to_see_cert) {
+ return false;
+ }
+ /* the trust the one cert from server context: */
+ try {
+ trustone_ctx = SSLContext.getInstance("SSL");
+ trustone_ctx.init(mykey, trustOneCert, new
+ java.security.SecureRandom());
+
+ if (trustallCerts != null && trusturlCerts != null) {
+ if (trustallCerts.length == 1 && trusturlCerts.length == 1) {
+ if (trustallCerts[0].equals(trusturlCerts[0])) {
+ System.out.println(msg);
+ return true;
+ }
+ }
+ } catch (Exception e) {
+ String msg = "SSL trustone_ctx FAILED.";
+ dbg(msg);
+ throw new Exception(msg);
+ }
+ return false;
+ }
+
+ public void check_for_proxy() {
+ /*
+ * we call this early on to 1) check for a proxy, 2) grab
+ * Browser/JVM accepted HTTPS cert.
+ */
+ public void check_for_proxy_and_grab_vnc_server_cert() {
+
+ boolean result = false;
+
+ trusturlCerts = null;
+ proxy_in_use = false;
+
+ if (viewer.ignoreProxy) {
+ /* applet param says skip it. */
+ /* the downside is we do not set trusturlCerts for comparison later... */
+ /* nor do we autodetect x11vnc for GET=1. */
+ return;
+ }
+
+ dbg("------------------------------------------------");
+ dbg("Into check_for_proxy_and_grab_vnc_server_cert():");
+
+ dbg("TRYING HTTPS:");
+ String ustr = "https://" + host + ":";
+ if (viewer.httpsPort != null) {
+ ustr += viewer.httpsPort;
+ } else {
+ ustr += port; // hmmm
+ ustr += port;
+ }
+ ustr += viewer.urlPrefix + "/check.https.proxy.connection";
+ dbg("ustr is: " + ustr);
+
+
+ try {
+ /* prepare for an HTTPS URL connection to host:port */
+ URL url = new URL(ustr);
+ HttpsURLConnection https = (HttpsURLConnection)
+ url.openConnection();
+ HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
+
+ if (mykey != null) {
+ /* with oneTimeKey (mykey) we can't use the default SSL context */
+ if (trustsrvCerts != null) {
+ dbg("passing trustsrv_ctx to HttpsURLConnection to provide client cert.");
+ https.setSSLSocketFactory(trustsrv_ctx.getSocketFactory());
+ } else if (trustloc_ctx != null) {
+ dbg("passing trustloc_ctx to HttpsURLConnection to provide client cert.");
+ https.setSSLSocketFactory(trustloc_ctx.getSocketFactory());
+ }
+ }
+
+ https.setUseCaches(false);
+ https.setRequestMethod("GET");
+ https.setRequestProperty("Pragma", "No-Cache");
+ https.setRequestProperty("Proxy-Connection",
+ "Keep-Alive");
+ https.setRequestProperty("Proxy-Connection", "Keep-Alive");
+ https.setDoInput(true);
+
+ dbg("trying https.connect()");
+ https.connect();
+
+ dbg("trying https.getServerCertificates()");
+ trusturlCerts = https.getServerCertificates();
+
+ if (trusturlCerts == null) {
+ dbg("set trusturlCerts to null...");
+ dbg("set trusturlCerts to null!");
+ } else {
+ dbg("set trusturlCerts to non-null");
+ }
+
+ if (https.usingProxy()) {
+ proxy_in_use = true;
+ proxy_is_https = true;
+ dbg("HTTPS proxy in use. There may be connection problems.");
+ dbg("An HTTPS proxy is in use. There may be connection problems.");
+ }
+
+ dbg("trying https.getContent()");
+ Object output = https.getContent();
+ dbg("trying https.disconnect()");
+ https.disconnect();
+ result = true;
+ if (! viewer.GET) {
+ String header = https.getHeaderField("VNC-Server");
+ if (header != null && header.startsWith("x11vnc")) {
+ dbg("detected x11vnc server (1), setting GET=1");
+ viewer.GET = true;
+ }
+ }
+
+ } catch(Exception e) {
+ dbg("HttpsURLConnection: " + e.getMessage());
+ }
+
+ if (proxy_in_use) {
+ dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
+ dbg("------------------------------------------------");
+ return;
+ } else if (trusturlCerts != null && !viewer.forceProxy) {
+ /* Allow user to require HTTP check? use forceProxy for now. */
+ dbg("SKIPPING HTTP PROXY CHECK: got trusturlCerts, assuming proxy info is correct.");
+ dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
+ dbg("------------------------------------------------");
+ return;
+ }
+
+ /*
+ * XXX need to remember scenario where this extra check
+ * gives useful info. User's Browser proxy settings?
+ */
+ dbg("TRYING HTTP:");
+ ustr = "http://" + host + ":" + port;
+ ustr += viewer.urlPrefix + "/index.vnc";
+ dbg("ustr is: " + ustr);
+
+ try {
+ /* prepare for an HTTP URL connection to the same host:port (but not httpsPort) */
+ URL url = new URL(ustr);
+ HttpURLConnection http = (HttpURLConnection)
+ url.openConnection();
......@@ -528,45 +707,64 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ http.setUseCaches(false);
+ http.setRequestMethod("GET");
+ http.setRequestProperty("Pragma", "No-Cache");
+ http.setRequestProperty("Proxy-Connection",
+ "Keep-Alive");
+ http.setRequestProperty("Proxy-Connection", "Keep-Alive");
+ http.setDoInput(true);
+
+ dbg("trying http.connect()");
+ http.connect();
+
+ if (http.usingProxy()) {
+ proxy_in_use = true;
+ proxy_is_https = false;
+ dbg("HTTP proxy in use. There may be connection problems.");
+ dbg("An HTTP proxy is in use. There may be connection problems.");
+ }
+ dbg("trying http.getContent()");
+ Object output = http.getContent();
+ dbg("trying http.disconnect()");
+ http.disconnect();
+
+ if (! viewer.GET) {
+ String header = http.getHeaderField("VNC-Server");
+ if (header != null && header.startsWith("x11vnc")) {
+ dbg("detected x11vnc server (2), setting GET=1");
+ viewer.GET = true;
+ }
+ }
+ } catch(Exception e) {
+ dbg("HttpURLConnection: " + e.getMessage());
+ dbg("HttpURLConnection: " + e.getMessage());
+ }
+ dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
+ dbg("------------------------------------------------");
+ }
+
+ public Socket connectSock() throws IOException {
+
+ /*
+ * first try a https connection to detect a proxy, and
+ * also grab the VNC server cert.
+ * grab the VNC server cert at the same time:
+ */
+ check_for_proxy();
+ check_for_proxy_and_grab_vnc_server_cert();
+
+ boolean srv_cert = false;
+
+ if (viewer.trustAllVncCerts) {
+ if (trustsrvCerts != null) {
+ /* applet parameter suppled serverCert */
+ dbg("viewer.trustSrvCert-0 using trustsrv_ctx");
+ factory = trustsrv_ctx.getSocketFactory();
+ srv_cert = true;
+ } else if (viewer.trustAllVncCerts) {
+ /* trust all certs (no checking) */
+ dbg("viewer.trustAllVncCerts-0 using trustall_ctx");
+ factory = trustall_ctx.getSocketFactory();
+ } else if (use_url_cert_for_auth && trusturlCerts != null) {
+ } else if (trusturlCerts != null) {
+ /* trust certs the Browser/JVM accepted in check_for_proxy... */
+ dbg("using trusturl_ctx");
+ factory = trusturl_ctx.getSocketFactory();
+ } else {
+ /* trust the local defaults */
+ dbg("using trustloc_ctx");
+ factory = trustloc_ctx.getSocketFactory();
+ }
+
+ socket = null;
+
+ try {
+ if (proxy_in_use && viewer.forceProxy) {
+ throw new Exception("forcing proxy (forceProxy)");
......@@ -585,7 +783,7 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ }
+
+ } catch (Exception esock) {
+ dbg("esock: " + esock.getMessage());
+ dbg("socket error: " + esock.getMessage());
+ if (proxy_in_use || viewer.CONNECT != null) {
+ proxy_failure = true;
+ if (proxy_in_use) {
......@@ -596,14 +794,17 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ try {
+ socket = proxy_socket(factory);
+ } catch (Exception e) {
+ dbg("err proxy_socket: " + e.getMessage());
+ dbg("proxy_socket error: " + e.getMessage());
+ }
+ } else {
+ /* n.b. socket is left in error state to cause ex. below. */
+ }
+ }
+
+ try {
+ socket.startHandshake();
+ dbg("Server Connection Verified on 1st try.");
+
+ dbg("The Server Connection Verified OK on 1st try.");
+
+ java.security.cert.Certificate[] currentTrustedCerts;
+ BrowserCertsDialog bcd;
......@@ -612,9 +813,13 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ currentTrustedCerts = sess.getPeerCertificates();
+
+ if (viewer.trustAllVncCerts) {
+ dbg("viewer.trustAllVncCerts-1");
+ dbg("viewer.trustAllVncCerts-1 keeping socket.");
+ } else if (currentTrustedCerts == null || currentTrustedCerts.length < 1) {
+ socket.close();
+ try {
+ socket.close();
+ } catch (Exception e) {
+ dbg("socket is grumpy.");
+ }
+ socket = null;
+ throw new SSLHandshakeException("no current certs");
+ }
......@@ -628,20 +833,28 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ }
+
+ if (viewer.trustAllVncCerts) {
+ dbg("viewer.trustAllVncCerts-2");
+ dbg("viewer.trustAllVncCerts-2 skipping browser certs dialog");
+ user_wants_to_see_cert = false;
+ } else if (viewer.serverCert != null && trustsrvCerts != null) {
+ dbg("viewer.serverCert-1 skipping browser certs dialog");
+ user_wants_to_see_cert = false;
+ } else if (viewer.trustUrlVncCert) {
+ dbg("viewer.trustUrlVncCert-1");
+ dbg("viewer.trustUrlVncCert-1 skipping browser certs dialog");
+ user_wants_to_see_cert = false;
+ } else {
+ /* have a dialog with the user: */
+ bcd = new BrowserCertsDialog(serv, host + ":" + port);
+ dbg("browser certs dialog START");
+ dbg("browser certs dialog begin.");
+ bcd.queryUser();
+ dbg("browser certs dialog DONE");
+ dbg("browser certs dialog finished.");
+
+ if (bcd.showCertDialog) {
+ String msg = "user wants to see cert";
+ dbg(msg);
+ user_wants_to_see_cert = true;
+ if (cert_fail == null) {
+ cert_fail = "user-view";
+ }
+ throw new SSLHandshakeException(msg);
+ } else {
+ user_wants_to_see_cert = false;
......@@ -650,18 +863,37 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ }
+
+ } catch (SSLHandshakeException eh) {
+ dbg("Could not automatically verify Server.");
+ dbg("SSLHandshakeException: could not automatically verify Server.");
+ dbg("msg: " + eh.getMessage());
+
+
+ /* send a cleanup string just in case: */
+ String getoutstr = "GET /index.vnc HTTP/1.0\r\nConnection: close\r\n\r\n";
+
+ OutputStream os = socket.getOutputStream();
+ os.write(getoutstr.getBytes());
+ socket.close();
+ try {
+ OutputStream os = socket.getOutputStream();
+ os.write(getoutstr.getBytes());
+ socket.close();
+ } catch (Exception e) {
+ dbg("socket is grumpy!");
+ }
+
+ /* reload */
+
+ socket = null;
+
+ String reason = null;
+
+ if (srv_cert) {
+ /* for serverCert usage we make this a fatal error. */
+ throw new IOException("Fatal: VNC Server's Cert does not match Applet Parameter 'serverCert=...'");
+ /* see below in TrustDialog were we describe this case to user anyway */
+ }
+
+ /*
+ * Reconnect, trusting any cert, so we can grab
+ * the cert to show it to the user. The connection
+ * the cert to show it to the user in a dialog
+ * for him to manually accept. This connection
+ * is not used for anything else.
+ */
+ factory = trustall_ctx.getSocketFactory();
......@@ -671,9 +903,18 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ socket = (SSLSocket) factory.createSocket(host, port);
+ }
+
+ if (debug_certs) {
+ dbg("trusturlCerts: " + trusturlCerts);
+ dbg("trustsrvCerts: " + trustsrvCerts);
+ }
+ if (trusturlCerts == null && cert_fail == null) {
+ cert_fail = "missing-certs";
+ }
+
+ try {
+ socket.startHandshake();
+ dbg("TrustAll Server Connection Verified.");
+
+ dbg("The TrustAll Server Cert-grab Connection (trivially) Verified OK.");
+
+ /* grab the cert: */
+ try {
......@@ -683,17 +924,24 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ throw new Exception("Could not get " +
+ "Peer Certificate");
+ }
+ if (debug_certs) {
+ dbg("trustallCerts: " + trustallCerts);
+ }
+
+ if (viewer.trustAllVncCerts) {
+ dbg("viewer.trustAllVncCerts-3");
+ dbg("viewer.trustAllVncCerts-3. skipping dialog, trusting everything.");
+ } else if (! browser_cert_match()) {
+ /*
+ * close socket now, we will reopen after
+ * dialog if user agrees to use the cert.
+ */
+ os = socket.getOutputStream();
+ os.write(getoutstr.getBytes());
+ socket.close();
+ try {
+ OutputStream os = socket.getOutputStream();
+ os.write(getoutstr.getBytes());
+ socket.close();
+ } catch (Exception e) {
+ dbg("socket is grumpy!!");
+ }
+ socket = null;
+
+ /* dialog with user to accept cert or not: */
......@@ -701,7 +949,27 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ TrustDialog td= new TrustDialog(host, port,
+ trustallCerts);
+
+ if (! td.queryUser()) {
+ if (cert_fail == null) {
+ ;
+ } else if (cert_fail.equals("user-view")) {
+ reason = "Reason for this Dialog:\n\n"
+ + " You Asked to View the Certificate.";
+ } else if (cert_fail.equals("server-cert-mismatch")) {
+ /* this is now fatal error, see above. */
+ reason = "Reason for this Dialog:\n\n"
+ + " The VNC Server's Certificate does not match the Certificate\n"
+ + " specified in the supplied 'serverCert' Applet Parameter.";
+ } else if (cert_fail.equals("cert-mismatch")) {
+ reason = "Reason for this Dialog:\n\n"
+ + " The VNC Server's Certificate does not match the Website's\n"
+ + " HTTPS Certificate (that you previously accepted; either\n"
+ + " manually or automatically via Certificate Authority.)";
+ } else if (cert_fail.equals("missing-certs")) {
+ reason = "Reason for this Dialog:\n\n"
+ + " Not all Certificates could be obtained to check.";
+ }
+
+ if (! td.queryUser(reason)) {
+ String msg = "User decided against it.";
+ dbg(msg);
+ throw new IOException(msg);
......@@ -709,24 +977,26 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ }
+
+ } catch (Exception ehand2) {
+ dbg("** Could not TrustAll Verify Server.");
+ dbg("** Could not TrustAll Verify Server!");
+
+ throw new IOException(ehand2.getMessage());
+ }
+
+ /* reload again: */
+
+ if (socket != null) {
+ try {
+ socket.close();
+ } catch (Exception e) {
+ ;
+ dbg("socket is grumpy!!!");
+ }
+ socket = null;
+ }
+
+ /*
+ * Now connect a 3rd time, using the cert
+ * retrieved during connection 2 (that the user
+ * likely blindly agreed to).
+ * retrieved during connection 2 (sadly, that
+ * the user likely blindly agreed to...)
+ */
+
+ factory = trustone_ctx.getSocketFactory();
......@@ -738,15 +1008,18 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+
+ try {
+ socket.startHandshake();
+ dbg("TrustAll Server Connection Verified #3.");
+ dbg("TrustAll/TrustOne Server Connection Verified #3.");
+
+ } catch (Exception ehand3) {
+ dbg("** Could not TrustAll Verify Server #3.");
+ dbg("** Could not TrustAll/TrustOne Verify Server #3.");
+
+ throw new IOException(ehand3.getMessage());
+ }
+ }
+
+ /* we have socket (possibly null) at this point, so proceed: */
+
+ /* handle x11vnc GET=1, if applicable: */
+ if (socket != null && viewer.GET) {
+ String str = "GET ";
+ str += viewer.urlPrefix;
......@@ -754,9 +1027,11 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ str += " HTTP/1.0\r\n";
+ str += "Pragma: No-Cache\r\n";
+ str += "\r\n";
+
+ System.out.println("sending GET: " + str);
+ OutputStream os = socket.getOutputStream();
+ String type = "os";
+
+ if (type == "os") {
+ os.write(str.getBytes());
+ os.flush();
......@@ -787,9 +1062,56 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ }
+
+ dbg("SSL returning socket to caller.");
+ dbg("");
+
+ /* could be null, let caller handle that. */
+ return (Socket) socket;
+ }
+
+ boolean browser_cert_match() {
+ String msg = "Browser URL accept previously accepted cert";
+
+ if (user_wants_to_see_cert) {
+ return false;
+ }
+
+ if (viewer.serverCert != null || trustsrvCerts != null) {
+ if (cert_fail == null) {
+ cert_fail = "server-cert-mismatch";
+ }
+ }
+ if (trustallCerts != null && trusturlCerts != null) {
+ if (trustallCerts.length == trusturlCerts.length) {
+ boolean ok = true;
+ /* check toath trustallCerts (socket) equals trusturlCerts (browser) */
+ for (int i = 0; i < trusturlCerts.length; i++) {
+ if (! trustallCerts[i].equals(trusturlCerts[i])) {
+ dbg("BCM: cert mismatch at i=" + i);
+ dbg("BCM: cert mismatch url" + trusturlCerts[i]);
+ dbg("BCM: cert mismatch all" + trustallCerts[i]);
+ ok = false;
+ }
+ }
+ if (ok) {
+ System.out.println(msg);
+ if (cert_fail == null) {
+ cert_fail = "did-not-fail";
+ }
+ return true;
+ } else {
+ if (cert_fail == null) {
+ cert_fail = "cert-mismatch";
+ }
+ return false;
+ }
+ }
+ }
+ if (cert_fail == null) {
+ cert_fail = "missing-certs";
+ }
+ return false;
+ }
+
+ private void dbg(String s) {
+ if (debug) {
+ System.out.println(s);
......@@ -807,6 +1129,8 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ return n;
+ }
+
+ /* this will do the proxy CONNECT negotiation and hook us up. */
+
+ private void proxy_helper(String proxyHost, int proxyPort) {
+
+ boolean proxy_auth = false;
......@@ -814,14 +1138,15 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ String hp = host + ":" + port;
+ dbg("proxy_helper: " + proxyHost + ":" + proxyPort + " hp: " + hp);
+
+ /* we loop here a few times trying for the password case */
+ for (int k=0; k < 2; k++) {
+ dbg("proxy_in_use psocket:");
+ dbg("proxy_in_use psocket: " + k);
+
+ if (proxySock != null) {
+ try {
+ proxySock.close();
+ } catch (Exception e) {
+ ;
+ dbg("proxy socket is grumpy.");
+ }
+ }
+
......@@ -834,7 +1159,7 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ String req1 = "CONNECT " + hp + " HTTP/1.1\r\n"
+ + "Host: " + hp + "\r\n";
+
+ dbg("requesting: " + req1);
+ dbg("requesting via proxy: " + req1);
+
+ if (proxy_auth) {
+ if (proxy_auth_string == null) {
......@@ -843,10 +1168,13 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ proxy_auth_string = pp.getAuth();
+ }
+ //dbg("auth1: " + proxy_auth_string);
+
+ String auth2 = Base64Coder.encodeString(proxy_auth_string);
+ //dbg("auth2: " + auth2);
+
+ req1 += "Proxy-Authorization: Basic " + auth2 + "\r\n";
+ //dbg("req1: " + req1);
+
+ dbg("added Proxy-Authorization: Basic ... to request");
+ }
+ req1 += "\r\n";
......@@ -869,9 +1197,10 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ }
+ }
+ } catch(Exception e) {
+ dbg("sock prob: " + e.getMessage());
+ dbg("some proxy socket problem: " + e.getMessage());
+ }
+
+ /* read the rest of the HTTP headers */
+ while (true) {
+ String line = readline(proxy_is);
+ dbg("proxy line: " + line.trim());
......@@ -891,6 +1220,7 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ }
+ }
+ if (!proxy_auth || proxy_auth_basic_realm.equals("")) {
+ /* we only try once for the non-password case: */
+ break;
+ }
+ }
......@@ -908,6 +1238,7 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ try {
+ props = System.getProperties();
+ } catch (Exception e) {
+ /* sandboxed applet might not be able to read it. */
+ dbg("props failed: " + e.getMessage());
+ }
+ if (viewer.proxyHost != null) {
......@@ -924,6 +1255,8 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ props.list(System.out);
+ dbg("\n---------------\n\n");
+
+ /* scrape throught properties looking for proxy info: */
+
+ for (Enumeration e = props.propertyNames(); e.hasMoreElements(); ) {
+ String s = (String) e.nextElement();
+ String v = System.getProperty(s);
......@@ -1046,7 +1379,7 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ }
+ }
+ } catch(Exception e) {
+ dbg("sock prob2: " + e.getMessage());
+ dbg("proxy socket problem-2: " + e.getMessage());
+ }
+
+ while (true) {
......@@ -1130,7 +1463,7 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ msg = "VNC Server " + host + ":" + port + " Not Verified";
+ }
+
+ public boolean queryUser() {
+ public boolean queryUser(String reason) {
+
+ /* create and display the dialog for unverified cert. */
+
......@@ -1143,6 +1476,9 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ CertInfo ci = new CertInfo(trustallCerts[0]);
+ infostr = ci.get_certinfo("all");
+ }
+ if (reason != null) {
+ reason += "\n\n";
+ }
+
+ text = "\n"
++ "Unable to verify the identity of\n"
......@@ -1153,28 +1489,38 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
++ "\n"
++ "as a trusted VNC server.\n"
++ "\n"
++ "This may be due to:\n"
++ reason
++ "In General not being able to verify the VNC Server and/or your seeing this Dialog\n"
++ "is due to one of the following:\n"
++ "\n"
++ " - Your requesting to View the Certificate before accepting.\n"
++ "\n"
++ " - The VNC server using a Self-Signed Certificate.\n"
++ " - The VNC server is using a Self-Signed Certificate or a Certificate\n"
++ " Authority not recognized by your Web Browser or Java Plugin runtime.\n"
++ "\n"
++ " - The use of an Apache SSL portal scheme employing CONNECT proxying AND\n"
++ " the Apache Web server has a certificate *different* from the VNC server's.\n"
++ "\n"
++ " - The VNC server using a Certificate Authority not recognized by your\n"
++ " Browser or Java Plugin runtime.\n"
++ " - No previously accepted Certificate (via Web Broswer/Java Plugin) could be\n"
++ " obtained by this applet to compare the VNC Server Certificate against.\n"
++ "\n"
++ " - The use of an Apache SSL portal employing CONNECT proxying and the\n"
++ " Apache web server has a certificate different from the VNC server's. \n"
++ " - The VNC Server's Certificate does not match the one specified in the\n"
++ " supplied 'serverCert' Java Applet Parameter.\n"
++ "\n"
++ " - A Man-In-The-Middle attack impersonating as the VNC server you wish\n"
++ " - A Man-In-The-Middle attack impersonating as the VNC server that you wish\n"
++ " to connect to. (Wouldn't that be exciting!!)\n"
++ "\n"
++ "By safely copying the VNC server's Certificate (or using a common\n"
++ "Certificate Authority certificate) you can configure your Web Browser or\n"
++ "Java Plugin to automatically authenticate this Server.\n"
++ "By safely copying the VNC server's Certificate (or using a common Certificate\n"
++ "Authority certificate) you can configure your Web Browser and Java Plugin to\n"
++ "automatically authenticate this VNC Server.\n"
++ "\n"
++ "If you do so, then you will only have to click \"Yes\" when this VNC Viewer\n"
++ "applet asks you whether to trust your Browser/Java Plugin's acceptance of the\n"
++ "certificate (except for the Apache portal case above where they don't match.)\n"
++ "\n"
++ "If you do so, then you will only have to click \"Yes\" when this VNC\n"
++ "Viewer applet asks you whether to trust your Browser/Java Plugin's\n"
++ "acceptance of the certificate. (except for the Apache portal case above.)\n"
++ "You can also set the applet parameter 'trustUrlVncCert=yes' to automatically\n"
++ "accept certificates already accepted/trusted by your Web Browser/Java Plugin,\n"
++ "and thereby see no dialog from this VNC Viewer applet.\n"
+;
+
+ /* the accept / do-not-accept radio buttons: */
......@@ -1210,7 +1556,7 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+ label.setFont(new Font("Helvetica", Font.BOLD, 16));
+
+ /* textarea in the middle */
+ textarea = new TextArea(text, 36, 64,
+ textarea = new TextArea(text, 38, 64,
+ TextArea.SCROLLBARS_VERTICAL_ONLY);
+ textarea.setEditable(false);
+
......@@ -1562,22 +1908,19 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+m += "\n";
+m += "on its own.\n";
+m += "\n";
+m += "However, it has noticed that your Web Browser or Java VM Plugin\n";
+m += "However, it has noticed that your Web Browser and/or Java VM Plugin\n";
+m += "has previously accepted the same certificate. You may have set\n";
+m += "this up permanently or just for this session, or the server\n";
+m += "certificate was signed by a CA cert that your Web Browser or\n";
+m += "Java VM Plugin has.\n";
+m += "\n";
+m += "If the VNC Server connection times out while you are reading this\n";
+m += "dialog, then restart the connection and try again.\n";
+m += "\n";
+m += "Should this VNC Viewer applet now connect to the above VNC server?\n";
+m += "\n";
+
+// String m = "\nShould this VNC Viewer applet use your Browser/JVM certs to\n";
+// m += "authenticate the VNC Server:\n";
+// m += "\n " + hostport + "\n\n " + vncServer + "\n\n";
+// m += "(NOTE: this *includes* any certs you have Just Now accepted in a\n";
+// m += "dialog box with your Web Browser or Java Applet Plugin)\n\n";
+
+ TextArea textarea = new TextArea(m, 20, 64,
+ TextArea textarea = new TextArea(m, 22, 64,
+ TextArea.SCROLLBARS_VERTICAL_ONLY);
+ textarea.setEditable(false);
+ yes = new Button("Yes");
......@@ -1789,8 +2132,16 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL
+}
diff -x VncCanvas.java -Naur vnc_javasrc.orig/VncViewer.java vnc_javasrc/VncViewer.java
--- vnc_javasrc.orig/VncViewer.java 2004-03-04 08:34:25.000000000 -0500
+++ vnc_javasrc/VncViewer.java 2010-02-22 19:25:19.000000000 -0500
@@ -80,7 +80,7 @@
+++ vnc_javasrc/VncViewer.java 2010-03-20 19:49:14.000000000 -0400
@@ -29,6 +29,7 @@
import java.awt.event.*;
import java.io.*;
import java.net.*;
+import java.util.*;
public class VncViewer extends java.applet.Applet
implements java.lang.Runnable, WindowListener {
@@ -80,7 +81,7 @@
// Variables read from parameter values.
String socketFactory;
String host;
......@@ -1799,7 +2150,7 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/VncViewer.java vnc_javasrc/VncView
boolean showControls;
boolean offerRelogin;
boolean showOfflineDesktop;
@@ -88,6 +88,20 @@
@@ -88,6 +89,21 @@
int deferCursorUpdates;
int deferUpdateRequests;
......@@ -1809,6 +2160,7 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/VncViewer.java vnc_javasrc/VncView
+ String urlPrefix;
+ String httpsPort;
+ String oneTimeKey;
+ String serverCert;
+ String proxyHost;
+ String proxyPort;
+ boolean forceProxy;
......@@ -1820,9 +2172,12 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/VncViewer.java vnc_javasrc/VncView
// Reference to this applet for inter-applet communication.
public static java.applet.Applet refApplet;
@@ -591,8 +605,25 @@
@@ -590,9 +606,28 @@
fatalError("HOST parameter not specified");
}
}
+ Date d = new Date();
+ System.out.println("-\nSSL VNC Java Applet starting. " + d);
- String str = readParameter("PORT", true);
- port = Integer.parseInt(str);
......@@ -1848,7 +2203,7 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/VncViewer.java vnc_javasrc/VncView
if (inAnApplet) {
str = readParameter("Open New Window", false);
@@ -626,6 +657,101 @@
@@ -626,6 +661,106 @@
// SocketFactory.
socketFactory = readParameter("SocketFactory", false);
......@@ -1891,7 +2246,12 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/VncViewer.java vnc_javasrc/VncView
+
+ oneTimeKey = readParameter("oneTimeKey", false);
+ if (oneTimeKey != null) {
+ System.out.println("oneTimeKey: is set");
+ System.out.println("oneTimeKey is set.");
+ }
+
+ serverCert = readParameter("serverCert", false);
+ if (serverCert != null) {
+ System.out.println("serverCert is set.");
+ }
+
+ forceProxy = false;
......
......@@ -2644,8 +2644,8 @@ diff -Naur JavaViewer.orig/RfbProto.java JavaViewer/RfbProto.java
// }
diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
--- JavaViewer.orig/SSLSocketToMe.java 1969-12-31 19:00:00.000000000 -0500
+++ JavaViewer/SSLSocketToMe.java 2010-02-22 20:03:11.000000000 -0500
@@ -0,0 +1,1712 @@
+++ JavaViewer/SSLSocketToMe.java 2010-03-19 12:52:08.000000000 -0400
@@ -0,0 +1,2055 @@
+/*
+ * SSLSocketToMe.java: add SSL encryption to Java VNC Viewer.
+ *
......@@ -2689,7 +2689,9 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ String host;
+ int port;
+ VncViewer viewer;
+
+ boolean debug = true;
+ boolean debug_certs = false;
+
+ /* sockets */
+ SSLSocket socket = null;
......@@ -2697,11 +2699,11 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+
+ /* fallback for Proxy connection */
+ boolean proxy_in_use = false;
+ boolean proxy_is_https = false;
+ boolean proxy_failure = false;
+ public DataInputStream is = null;
+ public OutputStream os = null;
+
+ /* strings from user WRT proxy: */
+ String proxy_auth_string = null;
+ String proxy_dialog_host = null;
+ int proxy_dialog_port = 0;
......@@ -2713,21 +2715,28 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ /* trust contexts */
+ SSLContext trustloc_ctx;
+ SSLContext trustall_ctx;
+ SSLContext trustsrv_ctx;
+ SSLContext trusturl_ctx;
+ SSLContext trustone_ctx;
+
+ /* corresponding trust managers */
+ TrustManager[] trustAllCerts;
+ TrustManager[] trustSrvCert;
+ TrustManager[] trustUrlCert;
+ TrustManager[] trustOneCert;
+
+ boolean use_url_cert_for_auth = true;
+ /* client-side SSL auth key (oneTimeKey=...) */
+ KeyManager[] mykey = null;
+
+ boolean user_wants_to_see_cert = true;
+ boolean debug_certs = false;
+ String cert_fail = null;
+
+ /* cert(s) we retrieve from VNC server */
+ /* cert(s) we retrieve from Web server, VNC server, or serverCert param: */
+ java.security.cert.Certificate[] trustallCerts = null;
+ java.security.cert.Certificate[] trustsrvCerts = null;
+ java.security.cert.Certificate[] trusturlCerts = null;
+
+ /* utility to decode hex oneTimeKey=... and serverCert=... */
+ byte[] hex2bytes(String s) {
+ byte[] bytes = new byte[s.length()/2];
+ for (int i=0; i<s.length()/2; i++) {
......@@ -2760,11 +2769,15 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+
+ dbg("SSL startup: " + host + " " + port);
+
+ /* create trust managers used if initial handshake fails: */
+
+ /* create trust managers to be used if initial handshake fails: */
+
+ trustAllCerts = new TrustManager[] {
+ /*
+ * this one accepts everything.
+ * this one accepts everything. Only used if user
+ * has disabled checking (trustAllVncCerts=yes)
+ * or when we grab the cert to show it to them in
+ * a dialog and ask them to manually verify/accept it.
+ */
+ new X509TrustManager() {
+ public java.security.cert.X509Certificate[]
......@@ -2787,8 +2800,9 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+
+ trustUrlCert = new TrustManager[] {
+ /*
+ * this one accepts only the retrieved server cert
+ * by SSLSocket by this applet.
+ * this one accepts only the retrieved server
+ * cert by SSLSocket by this applet and stored in
+ * trusturlCerts.
+ */
+ new X509TrustManager() {
+ public java.security.cert.X509Certificate[]
......@@ -2798,11 +2812,12 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ public void checkClientTrusted(
+ java.security.cert.X509Certificate[] certs,
+ String authType) throws CertificateException {
+ throw new CertificateException("No Clients");
+ throw new CertificateException("No Clients (URL)");
+ }
+ public void checkServerTrusted(
+ java.security.cert.X509Certificate[] certs,
+ String authType) throws CertificateException {
+ /* we want to check 'certs' against 'trusturlCerts' */
+ if (trusturlCerts == null) {
+ throw new CertificateException(
+ "No Trust url Certs array.");
......@@ -2830,6 +2845,9 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ dbg("URL: cert mismatch at i=" + i);
+ dbg("URL: cert mismatch cert" + certs[i]);
+ dbg("URL: cert mismatch url" + trusturlCerts[i]);
+ if (cert_fail == null) {
+ cert_fail = "cert-mismatch";
+ }
+ }
+ if (debug_certs) {
+ dbg("\n***********************************************");
......@@ -2848,10 +2866,86 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ }
+ }
+ };
+
+ trustSrvCert = new TrustManager[] {
+ /*
+ * this one accepts cert given to us in the serverCert
+ * Applet Parameter we were started with. It is
+ * currently a fatal error if the VNC Server's cert
+ * doesn't match it.
+ */
+ new X509TrustManager() {
+ public java.security.cert.X509Certificate[]
+ getAcceptedIssuers() {
+ return null;
+ }
+ public void checkClientTrusted(
+ java.security.cert.X509Certificate[] certs,
+ String authType) throws CertificateException {
+ throw new CertificateException("No Clients (SRV)");
+ }
+ public void checkServerTrusted(
+ java.security.cert.X509Certificate[] certs,
+ String authType) throws CertificateException {
+ /* we want to check 'certs' against 'trustsrvCerts' */
+ if (trustsrvCerts == null) {
+ throw new CertificateException(
+ "No Trust srv Certs array.");
+ }
+ if (trustsrvCerts.length < 1) {
+ throw new CertificateException(
+ "No Trust srv Certs.");
+ }
+ if (certs == null) {
+ throw new CertificateException(
+ "No this-certs array.");
+ }
+ if (certs.length < 1) {
+ throw new CertificateException(
+ "No this-certs Certs.");
+ }
+ if (certs.length != trustsrvCerts.length) {
+ throw new CertificateException(
+ "certs.length != trustsrvCerts.length " + certs.length + " " + trustsrvCerts.length);
+ }
+ boolean ok = true;
+ for (int i = 0; i < certs.length; i++) {
+ if (! trustsrvCerts[i].equals(certs[i])) {
+ ok = false;
+ dbg("SRV: cert mismatch at i=" + i);
+ dbg("SRV: cert mismatch cert" + certs[i]);
+ dbg("SRV: cert mismatch srv" + trustsrvCerts[i]);
+ if (cert_fail == null) {
+ cert_fail = "server-cert-mismatch";
+ }
+ }
+ if (debug_certs) {
+ dbg("\n***********************************************");
+ dbg("SRV: cert info at i=" + i);
+ dbg("SRV: cert info cert" + certs[i]);
+ dbg("===============================================");
+ dbg("SRV: cert info srv" + trustsrvCerts[i]);
+ dbg("***********************************************");
+ }
+ }
+ if (!ok) {
+ throw new CertificateException(
+ "Server Cert Chain != serverCert Applet Parameter Cert Chain.");
+ }
+ dbg("SRV: trustsrvCerts[i] matches certs[i] i=0:" + (certs.length-1));
+ }
+ }
+ };
+
+ trustOneCert = new TrustManager[] {
+ /*
+ * this one accepts only the retrieved server cert
+ * by SSLSocket by this applet.
+ * this one accepts only the retrieved server
+ * cert by SSLSocket by this applet we stored in
+ * trustallCerts that user has accepted or applet
+ * parameter trustAllVncCerts=yes is set. This is
+ * for when we reconnect after the user has manually
+ * accepted the trustall cert in the dialog (or set
+ * trustAllVncCerts=yes applet param.)
+ */
+ new X509TrustManager() {
+ public java.security.cert.X509Certificate[]
......@@ -2861,11 +2955,12 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ public void checkClientTrusted(
+ java.security.cert.X509Certificate[] certs,
+ String authType) throws CertificateException {
+ throw new CertificateException("No Clients");
+ throw new CertificateException("No Clients (ONE)");
+ }
+ public void checkServerTrusted(
+ java.security.cert.X509Certificate[] certs,
+ String authType) throws CertificateException {
+ /* we want to check 'certs' against 'trustallCerts' */
+ if (trustallCerts == null) {
+ throw new CertificateException(
+ "No Trust All Server Certs array.");
......@@ -2913,20 +3008,20 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ };
+
+ /*
+ * They are used:
+ * The above TrustManagers are used:
+ *
+ * 1) to retrieve the server cert in case of failure to
+ * display it to the user.
+ * display it to the user in a dialog.
+ * 2) to subsequently connect to the server if user agrees.
+ */
+
+ KeyManager[] mykey = null;
+
+ /*
+ * build oneTimeKey cert+key if supplied in applet parameter:
+ */
+ if (viewer.oneTimeKey != null && viewer.oneTimeKey.equals("PROMPT")) {
+ ClientCertDialog d = new ClientCertDialog();
+ viewer.oneTimeKey = d.queryUser();
+ }
+
+ if (viewer.oneTimeKey != null && viewer.oneTimeKey.indexOf(",") > 0) {
+ int idx = viewer.oneTimeKey.indexOf(",");
+
......@@ -2938,15 +3033,18 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec ( key );
+ PrivateKey ff = kf.generatePrivate (keysp);
+ //dbg("ff " + ff);
+ String cert_str = new String(cert);
+ if (debug_certs) {
+ dbg("one time key " + ff);
+ }
+
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ Collection c = cf.generateCertificates(new ByteArrayInputStream(cert));
+ Certificate[] certs = new Certificate[c.toArray().length];
+ if (c.size() == 1) {
+ Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert));
+ //dbg("tmpcert" + tmpcert);
+ if (debug_certs) {
+ dbg("one time cert" + tmpcert);
+ }
+ certs[0] = tmpcert;
+ } else {
+ certs = (Certificate[]) c.toArray();
......@@ -2962,12 +3060,54 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ mykey = kmf.getKeyManagers();
+ }
+
+ /*
+ * build serverCert cert if supplied in applet parameter:
+ */
+ if (viewer.serverCert != null) {
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ byte[] cert = hex2bytes(viewer.serverCert);
+ Collection c = cf.generateCertificates(new ByteArrayInputStream(cert));
+ trustsrvCerts = new Certificate[c.toArray().length];
+ if (c.size() == 1) {
+ Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert));
+ trustsrvCerts[0] = tmpcert;
+ } else {
+ trustsrvCerts = (Certificate[]) c.toArray();
+ }
+ }
+
+ /* trust loc certs: */
+ /* the trust loc certs context: */
+ try {
+ trustloc_ctx = SSLContext.getInstance("SSL");
+ trustloc_ctx.init(mykey, null, new
+ java.security.SecureRandom());
+
+ /*
+ * below is a failed attempt to get jvm's default
+ * trust manager using null (below) makes it so
+ * for HttpsURLConnection the server cannot be
+ * verified (no prompting.)
+ */
+ if (false) {
+ boolean didit = false;
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ tmf.init((KeyStore) null);
+ TrustManager [] tml = tmf.getTrustManagers();
+ for (int i = 0; i < tml.length; i++) {
+ TrustManager tm = tml[i];
+ if (tm instanceof X509TrustManager) {
+ TrustManager tm1[] = new TrustManager[1];
+ tm1[0] = tm;
+ trustloc_ctx.init(mykey, tm1, null);
+ didit = true;
+ break;
+ }
+ }
+ if (!didit) {
+ trustloc_ctx.init(mykey, null, null);
+ }
+ } else {
+ /* we have to set trust manager to null */
+ trustloc_ctx.init(mykey, null, null);
+ }
+
+ } catch (Exception e) {
+ String msg = "SSL trustloc_ctx FAILED.";
......@@ -2975,7 +3115,7 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ throw new Exception(msg);
+ }
+
+ /* trust all certs: */
+ /* the trust all certs context: */
+ try {
+ trustall_ctx = SSLContext.getInstance("SSL");
+ trustall_ctx.init(mykey, trustAllCerts, new
......@@ -2987,7 +3127,7 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ throw new Exception(msg);
+ }
+
+ /* trust url certs: */
+ /* the trust url certs context: */
+ try {
+ trusturl_ctx = SSLContext.getInstance("SSL");
+ trusturl_ctx.init(mykey, trustUrlCert, new
......@@ -2999,99 +3139,138 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ throw new Exception(msg);
+ }
+
+ /* trust the one cert from server: */
+ /* the trust srv certs context: */
+ try {
+ trustone_ctx = SSLContext.getInstance("SSL");
+ trustone_ctx.init(mykey, trustOneCert, new
+ trustsrv_ctx = SSLContext.getInstance("SSL");
+ trustsrv_ctx.init(mykey, trustSrvCert, new
+ java.security.SecureRandom());
+
+ } catch (Exception e) {
+ String msg = "SSL trustone_ctx FAILED.";
+ String msg = "SSL trustsrv_ctx FAILED.";
+ dbg(msg);
+ throw new Exception(msg);
+ }
+ }
+
+ boolean browser_cert_match() {
+ String msg = "Browser URL accept previously accepted cert";
+
+ if (user_wants_to_see_cert) {
+ return false;
+ }
+ /* the trust the one cert from server context: */
+ try {
+ trustone_ctx = SSLContext.getInstance("SSL");
+ trustone_ctx.init(mykey, trustOneCert, new
+ java.security.SecureRandom());
+
+ if (trustallCerts != null && trusturlCerts != null) {
+ if (trustallCerts.length == 1 && trusturlCerts.length == 1) {
+ if (trustallCerts[0].equals(trusturlCerts[0])) {
+ System.out.println(msg);
+ return true;
+ }
+ }
+ } catch (Exception e) {
+ String msg = "SSL trustone_ctx FAILED.";
+ dbg(msg);
+ throw new Exception(msg);
+ }
+ return false;
+ }
+
+ public void check_for_proxy() {
+ /*
+ * we call this early on to 1) check for a proxy, 2) grab
+ * Browser/JVM accepted HTTPS cert.
+ */
+ public void check_for_proxy_and_grab_vnc_server_cert() {
+
+ boolean result = false;
+
+ trusturlCerts = null;
+ proxy_in_use = false;
+
+ if (viewer.ignoreProxy) {
+ /* applet param says skip it. */
+ /* the downside is we do not set trusturlCerts for comparison later... */
+ /* nor do we autodetect x11vnc for GET=1. */
+ return;
+ }
+
+ dbg("------------------------------------------------");
+ dbg("Into check_for_proxy_and_grab_vnc_server_cert():");
+
+ dbg("TRYING HTTPS:");
+ String ustr = "https://" + host + ":";
+ if (viewer.httpsPort != null) {
+ ustr += viewer.httpsPort;
+ } else {
+ ustr += port; // hmmm
+ ustr += port;
+ }
+ ustr += viewer.urlPrefix + "/check.https.proxy.connection";
+ dbg("ustr is: " + ustr);
+
+
+ try {
+ /* prepare for an HTTPS URL connection to host:port */
+ URL url = new URL(ustr);
+ HttpsURLConnection https = (HttpsURLConnection)
+ url.openConnection();
+ HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
+
+ if (mykey != null) {
+ /* with oneTimeKey (mykey) we can't use the default SSL context */
+ if (trustsrvCerts != null) {
+ dbg("passing trustsrv_ctx to HttpsURLConnection to provide client cert.");
+ https.setSSLSocketFactory(trustsrv_ctx.getSocketFactory());
+ } else if (trustloc_ctx != null) {
+ dbg("passing trustloc_ctx to HttpsURLConnection to provide client cert.");
+ https.setSSLSocketFactory(trustloc_ctx.getSocketFactory());
+ }
+ }
+
+ https.setUseCaches(false);
+ https.setRequestMethod("GET");
+ https.setRequestProperty("Pragma", "No-Cache");
+ https.setRequestProperty("Proxy-Connection",
+ "Keep-Alive");
+ https.setRequestProperty("Proxy-Connection", "Keep-Alive");
+ https.setDoInput(true);
+
+ dbg("trying https.connect()");
+ https.connect();
+
+ dbg("trying https.getServerCertificates()");
+ trusturlCerts = https.getServerCertificates();
+
+ if (trusturlCerts == null) {
+ dbg("set trusturlCerts to null...");
+ dbg("set trusturlCerts to null!");
+ } else {
+ dbg("set trusturlCerts to non-null");
+ }
+
+ if (https.usingProxy()) {
+ proxy_in_use = true;
+ proxy_is_https = true;
+ dbg("HTTPS proxy in use. There may be connection problems.");
+ dbg("An HTTPS proxy is in use. There may be connection problems.");
+ }
+
+ dbg("trying https.getContent()");
+ Object output = https.getContent();
+ dbg("trying https.disconnect()");
+ https.disconnect();
+ result = true;
+ if (! viewer.GET) {
+ String header = https.getHeaderField("VNC-Server");
+ if (header != null && header.startsWith("x11vnc")) {
+ dbg("detected x11vnc server (1), setting GET=1");
+ viewer.GET = true;
+ }
+ }
+
+ } catch(Exception e) {
+ dbg("HttpsURLConnection: " + e.getMessage());
+ }
+
+ if (proxy_in_use) {
+ dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
+ dbg("------------------------------------------------");
+ return;
+ } else if (trusturlCerts != null && !viewer.forceProxy) {
+ /* Allow user to require HTTP check? use forceProxy for now. */
+ dbg("SKIPPING HTTP PROXY CHECK: got trusturlCerts, assuming proxy info is correct.");
+ dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
+ dbg("------------------------------------------------");
+ return;
+ }
+
+ /*
+ * XXX need to remember scenario where this extra check
+ * gives useful info. User's Browser proxy settings?
+ */
+ dbg("TRYING HTTP:");
+ ustr = "http://" + host + ":" + port;
+ ustr += viewer.urlPrefix + "/index.vnc";
+ dbg("ustr is: " + ustr);
+
+ try {
+ /* prepare for an HTTP URL connection to the same host:port (but not httpsPort) */
+ URL url = new URL(ustr);
+ HttpURLConnection http = (HttpURLConnection)
+ url.openConnection();
......@@ -3099,45 +3278,64 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ http.setUseCaches(false);
+ http.setRequestMethod("GET");
+ http.setRequestProperty("Pragma", "No-Cache");
+ http.setRequestProperty("Proxy-Connection",
+ "Keep-Alive");
+ http.setRequestProperty("Proxy-Connection", "Keep-Alive");
+ http.setDoInput(true);
+
+ dbg("trying http.connect()");
+ http.connect();
+
+ if (http.usingProxy()) {
+ proxy_in_use = true;
+ proxy_is_https = false;
+ dbg("HTTP proxy in use. There may be connection problems.");
+ dbg("An HTTP proxy is in use. There may be connection problems.");
+ }
+ dbg("trying http.getContent()");
+ Object output = http.getContent();
+ dbg("trying http.disconnect()");
+ http.disconnect();
+
+ if (! viewer.GET) {
+ String header = http.getHeaderField("VNC-Server");
+ if (header != null && header.startsWith("x11vnc")) {
+ dbg("detected x11vnc server (2), setting GET=1");
+ viewer.GET = true;
+ }
+ }
+ } catch(Exception e) {
+ dbg("HttpURLConnection: " + e.getMessage());
+ dbg("HttpURLConnection: " + e.getMessage());
+ }
+ dbg("exit check_for_proxy_and_grab_vnc_server_cert():");
+ dbg("------------------------------------------------");
+ }
+
+ public Socket connectSock() throws IOException {
+
+ /*
+ * first try a https connection to detect a proxy, and
+ * also grab the VNC server cert.
+ * grab the VNC server cert at the same time:
+ */
+ check_for_proxy();
+ check_for_proxy_and_grab_vnc_server_cert();
+
+ boolean srv_cert = false;
+
+ if (viewer.trustAllVncCerts) {
+ if (trustsrvCerts != null) {
+ /* applet parameter suppled serverCert */
+ dbg("viewer.trustSrvCert-0 using trustsrv_ctx");
+ factory = trustsrv_ctx.getSocketFactory();
+ srv_cert = true;
+ } else if (viewer.trustAllVncCerts) {
+ /* trust all certs (no checking) */
+ dbg("viewer.trustAllVncCerts-0 using trustall_ctx");
+ factory = trustall_ctx.getSocketFactory();
+ } else if (use_url_cert_for_auth && trusturlCerts != null) {
+ } else if (trusturlCerts != null) {
+ /* trust certs the Browser/JVM accepted in check_for_proxy... */
+ dbg("using trusturl_ctx");
+ factory = trusturl_ctx.getSocketFactory();
+ } else {
+ /* trust the local defaults */
+ dbg("using trustloc_ctx");
+ factory = trustloc_ctx.getSocketFactory();
+ }
+
+ socket = null;
+
+ try {
+ if (proxy_in_use && viewer.forceProxy) {
+ throw new Exception("forcing proxy (forceProxy)");
......@@ -3156,7 +3354,7 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ }
+
+ } catch (Exception esock) {
+ dbg("esock: " + esock.getMessage());
+ dbg("socket error: " + esock.getMessage());
+ if (proxy_in_use || viewer.CONNECT != null) {
+ proxy_failure = true;
+ if (proxy_in_use) {
......@@ -3167,14 +3365,17 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ try {
+ socket = proxy_socket(factory);
+ } catch (Exception e) {
+ dbg("err proxy_socket: " + e.getMessage());
+ dbg("proxy_socket error: " + e.getMessage());
+ }
+ } else {
+ /* n.b. socket is left in error state to cause ex. below. */
+ }
+ }
+
+ try {
+ socket.startHandshake();
+ dbg("Server Connection Verified on 1st try.");
+
+ dbg("The Server Connection Verified OK on 1st try.");
+
+ java.security.cert.Certificate[] currentTrustedCerts;
+ BrowserCertsDialog bcd;
......@@ -3183,9 +3384,13 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ currentTrustedCerts = sess.getPeerCertificates();
+
+ if (viewer.trustAllVncCerts) {
+ dbg("viewer.trustAllVncCerts-1");
+ dbg("viewer.trustAllVncCerts-1 keeping socket.");
+ } else if (currentTrustedCerts == null || currentTrustedCerts.length < 1) {
+ socket.close();
+ try {
+ socket.close();
+ } catch (Exception e) {
+ dbg("socket is grumpy.");
+ }
+ socket = null;
+ throw new SSLHandshakeException("no current certs");
+ }
......@@ -3199,20 +3404,28 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ }
+
+ if (viewer.trustAllVncCerts) {
+ dbg("viewer.trustAllVncCerts-2");
+ dbg("viewer.trustAllVncCerts-2 skipping browser certs dialog");
+ user_wants_to_see_cert = false;
+ } else if (viewer.serverCert != null && trustsrvCerts != null) {
+ dbg("viewer.serverCert-1 skipping browser certs dialog");
+ user_wants_to_see_cert = false;
+ } else if (viewer.trustUrlVncCert) {
+ dbg("viewer.trustUrlVncCert-1");
+ dbg("viewer.trustUrlVncCert-1 skipping browser certs dialog");
+ user_wants_to_see_cert = false;
+ } else {
+ /* have a dialog with the user: */
+ bcd = new BrowserCertsDialog(serv, host + ":" + port);
+ dbg("browser certs dialog START");
+ dbg("browser certs dialog begin.");
+ bcd.queryUser();
+ dbg("browser certs dialog DONE");
+ dbg("browser certs dialog finished.");
+
+ if (bcd.showCertDialog) {
+ String msg = "user wants to see cert";
+ dbg(msg);
+ user_wants_to_see_cert = true;
+ if (cert_fail == null) {
+ cert_fail = "user-view";
+ }
+ throw new SSLHandshakeException(msg);
+ } else {
+ user_wants_to_see_cert = false;
......@@ -3221,18 +3434,37 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ }
+
+ } catch (SSLHandshakeException eh) {
+ dbg("Could not automatically verify Server.");
+ dbg("SSLHandshakeException: could not automatically verify Server.");
+ dbg("msg: " + eh.getMessage());
+
+
+ /* send a cleanup string just in case: */
+ String getoutstr = "GET /index.vnc HTTP/1.0\r\nConnection: close\r\n\r\n";
+
+ OutputStream os = socket.getOutputStream();
+ os.write(getoutstr.getBytes());
+ socket.close();
+ try {
+ OutputStream os = socket.getOutputStream();
+ os.write(getoutstr.getBytes());
+ socket.close();
+ } catch (Exception e) {
+ dbg("socket is grumpy!");
+ }
+
+ /* reload */
+
+ socket = null;
+
+ String reason = null;
+
+ if (srv_cert) {
+ /* for serverCert usage we make this a fatal error. */
+ throw new IOException("Fatal: VNC Server's Cert does not match Applet Parameter 'serverCert=...'");
+ /* see below in TrustDialog were we describe this case to user anyway */
+ }
+
+ /*
+ * Reconnect, trusting any cert, so we can grab
+ * the cert to show it to the user. The connection
+ * the cert to show it to the user in a dialog
+ * for him to manually accept. This connection
+ * is not used for anything else.
+ */
+ factory = trustall_ctx.getSocketFactory();
......@@ -3242,9 +3474,18 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ socket = (SSLSocket) factory.createSocket(host, port);
+ }
+
+ if (debug_certs) {
+ dbg("trusturlCerts: " + trusturlCerts);
+ dbg("trustsrvCerts: " + trustsrvCerts);
+ }
+ if (trusturlCerts == null && cert_fail == null) {
+ cert_fail = "missing-certs";
+ }
+
+ try {
+ socket.startHandshake();
+ dbg("TrustAll Server Connection Verified.");
+
+ dbg("The TrustAll Server Cert-grab Connection (trivially) Verified OK.");
+
+ /* grab the cert: */
+ try {
......@@ -3254,17 +3495,24 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ throw new Exception("Could not get " +
+ "Peer Certificate");
+ }
+ if (debug_certs) {
+ dbg("trustallCerts: " + trustallCerts);
+ }
+
+ if (viewer.trustAllVncCerts) {
+ dbg("viewer.trustAllVncCerts-3");
+ dbg("viewer.trustAllVncCerts-3. skipping dialog, trusting everything.");
+ } else if (! browser_cert_match()) {
+ /*
+ * close socket now, we will reopen after
+ * dialog if user agrees to use the cert.
+ */
+ os = socket.getOutputStream();
+ os.write(getoutstr.getBytes());
+ socket.close();
+ try {
+ OutputStream os = socket.getOutputStream();
+ os.write(getoutstr.getBytes());
+ socket.close();
+ } catch (Exception e) {
+ dbg("socket is grumpy!!");
+ }
+ socket = null;
+
+ /* dialog with user to accept cert or not: */
......@@ -3272,7 +3520,27 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ TrustDialog td= new TrustDialog(host, port,
+ trustallCerts);
+
+ if (! td.queryUser()) {
+ if (cert_fail == null) {
+ ;
+ } else if (cert_fail.equals("user-view")) {
+ reason = "Reason for this Dialog:\n\n"
+ + " You Asked to View the Certificate.";
+ } else if (cert_fail.equals("server-cert-mismatch")) {
+ /* this is now fatal error, see above. */
+ reason = "Reason for this Dialog:\n\n"
+ + " The VNC Server's Certificate does not match the Certificate\n"
+ + " specified in the supplied 'serverCert' Applet Parameter.";
+ } else if (cert_fail.equals("cert-mismatch")) {
+ reason = "Reason for this Dialog:\n\n"
+ + " The VNC Server's Certificate does not match the Website's\n"
+ + " HTTPS Certificate (that you previously accepted; either\n"
+ + " manually or automatically via Certificate Authority.)";
+ } else if (cert_fail.equals("missing-certs")) {
+ reason = "Reason for this Dialog:\n\n"
+ + " Not all Certificates could be obtained to check.";
+ }
+
+ if (! td.queryUser(reason)) {
+ String msg = "User decided against it.";
+ dbg(msg);
+ throw new IOException(msg);
......@@ -3280,24 +3548,26 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ }
+
+ } catch (Exception ehand2) {
+ dbg("** Could not TrustAll Verify Server.");
+ dbg("** Could not TrustAll Verify Server!");
+
+ throw new IOException(ehand2.getMessage());
+ }
+
+ /* reload again: */
+
+ if (socket != null) {
+ try {
+ socket.close();
+ } catch (Exception e) {
+ ;
+ dbg("socket is grumpy!!!");
+ }
+ socket = null;
+ }
+
+ /*
+ * Now connect a 3rd time, using the cert
+ * retrieved during connection 2 (that the user
+ * likely blindly agreed to).
+ * retrieved during connection 2 (sadly, that
+ * the user likely blindly agreed to...)
+ */
+
+ factory = trustone_ctx.getSocketFactory();
......@@ -3309,15 +3579,18 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+
+ try {
+ socket.startHandshake();
+ dbg("TrustAll Server Connection Verified #3.");
+ dbg("TrustAll/TrustOne Server Connection Verified #3.");
+
+ } catch (Exception ehand3) {
+ dbg("** Could not TrustAll Verify Server #3.");
+ dbg("** Could not TrustAll/TrustOne Verify Server #3.");
+
+ throw new IOException(ehand3.getMessage());
+ }
+ }
+
+ /* we have socket (possibly null) at this point, so proceed: */
+
+ /* handle x11vnc GET=1, if applicable: */
+ if (socket != null && viewer.GET) {
+ String str = "GET ";
+ str += viewer.urlPrefix;
......@@ -3325,9 +3598,11 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ str += " HTTP/1.0\r\n";
+ str += "Pragma: No-Cache\r\n";
+ str += "\r\n";
+
+ System.out.println("sending GET: " + str);
+ OutputStream os = socket.getOutputStream();
+ String type = "os";
+
+ if (type == "os") {
+ os.write(str.getBytes());
+ os.flush();
......@@ -3358,9 +3633,56 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ }
+
+ dbg("SSL returning socket to caller.");
+ dbg("");
+
+ /* could be null, let caller handle that. */
+ return (Socket) socket;
+ }
+
+ boolean browser_cert_match() {
+ String msg = "Browser URL accept previously accepted cert";
+
+ if (user_wants_to_see_cert) {
+ return false;
+ }
+
+ if (viewer.serverCert != null || trustsrvCerts != null) {
+ if (cert_fail == null) {
+ cert_fail = "server-cert-mismatch";
+ }
+ }
+ if (trustallCerts != null && trusturlCerts != null) {
+ if (trustallCerts.length == trusturlCerts.length) {
+ boolean ok = true;
+ /* check toath trustallCerts (socket) equals trusturlCerts (browser) */
+ for (int i = 0; i < trusturlCerts.length; i++) {
+ if (! trustallCerts[i].equals(trusturlCerts[i])) {
+ dbg("BCM: cert mismatch at i=" + i);
+ dbg("BCM: cert mismatch url" + trusturlCerts[i]);
+ dbg("BCM: cert mismatch all" + trustallCerts[i]);
+ ok = false;
+ }
+ }
+ if (ok) {
+ System.out.println(msg);
+ if (cert_fail == null) {
+ cert_fail = "did-not-fail";
+ }
+ return true;
+ } else {
+ if (cert_fail == null) {
+ cert_fail = "cert-mismatch";
+ }
+ return false;
+ }
+ }
+ }
+ if (cert_fail == null) {
+ cert_fail = "missing-certs";
+ }
+ return false;
+ }
+
+ private void dbg(String s) {
+ if (debug) {
+ System.out.println(s);
......@@ -3378,6 +3700,8 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ return n;
+ }
+
+ /* this will do the proxy CONNECT negotiation and hook us up. */
+
+ private void proxy_helper(String proxyHost, int proxyPort) {
+
+ boolean proxy_auth = false;
......@@ -3385,14 +3709,15 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ String hp = host + ":" + port;
+ dbg("proxy_helper: " + proxyHost + ":" + proxyPort + " hp: " + hp);
+
+ /* we loop here a few times trying for the password case */
+ for (int k=0; k < 2; k++) {
+ dbg("proxy_in_use psocket:");
+ dbg("proxy_in_use psocket: " + k);
+
+ if (proxySock != null) {
+ try {
+ proxySock.close();
+ } catch (Exception e) {
+ ;
+ dbg("proxy socket is grumpy.");
+ }
+ }
+
......@@ -3405,7 +3730,7 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ String req1 = "CONNECT " + hp + " HTTP/1.1\r\n"
+ + "Host: " + hp + "\r\n";
+
+ dbg("requesting: " + req1);
+ dbg("requesting via proxy: " + req1);
+
+ if (proxy_auth) {
+ if (proxy_auth_string == null) {
......@@ -3414,10 +3739,13 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ proxy_auth_string = pp.getAuth();
+ }
+ //dbg("auth1: " + proxy_auth_string);
+
+ String auth2 = Base64Coder.encodeString(proxy_auth_string);
+ //dbg("auth2: " + auth2);
+
+ req1 += "Proxy-Authorization: Basic " + auth2 + "\r\n";
+ //dbg("req1: " + req1);
+
+ dbg("added Proxy-Authorization: Basic ... to request");
+ }
+ req1 += "\r\n";
......@@ -3440,9 +3768,10 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ }
+ }
+ } catch(Exception e) {
+ dbg("sock prob: " + e.getMessage());
+ dbg("some proxy socket problem: " + e.getMessage());
+ }
+
+ /* read the rest of the HTTP headers */
+ while (true) {
+ String line = readline(proxy_is);
+ dbg("proxy line: " + line.trim());
......@@ -3462,6 +3791,7 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ }
+ }
+ if (!proxy_auth || proxy_auth_basic_realm.equals("")) {
+ /* we only try once for the non-password case: */
+ break;
+ }
+ }
......@@ -3479,6 +3809,7 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ try {
+ props = System.getProperties();
+ } catch (Exception e) {
+ /* sandboxed applet might not be able to read it. */
+ dbg("props failed: " + e.getMessage());
+ }
+ if (viewer.proxyHost != null) {
......@@ -3495,6 +3826,8 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ props.list(System.out);
+ dbg("\n---------------\n\n");
+
+ /* scrape throught properties looking for proxy info: */
+
+ for (Enumeration e = props.propertyNames(); e.hasMoreElements(); ) {
+ String s = (String) e.nextElement();
+ String v = System.getProperty(s);
......@@ -3617,7 +3950,7 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ }
+ }
+ } catch(Exception e) {
+ dbg("sock prob2: " + e.getMessage());
+ dbg("proxy socket problem-2: " + e.getMessage());
+ }
+
+ while (true) {
......@@ -3701,7 +4034,7 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ msg = "VNC Server " + host + ":" + port + " Not Verified";
+ }
+
+ public boolean queryUser() {
+ public boolean queryUser(String reason) {
+
+ /* create and display the dialog for unverified cert. */
+
......@@ -3714,6 +4047,9 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ CertInfo ci = new CertInfo(trustallCerts[0]);
+ infostr = ci.get_certinfo("all");
+ }
+ if (reason != null) {
+ reason += "\n\n";
+ }
+
+ text = "\n"
++ "Unable to verify the identity of\n"
......@@ -3724,28 +4060,38 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
++ "\n"
++ "as a trusted VNC server.\n"
++ "\n"
++ "This may be due to:\n"
++ reason
++ "In General not being able to verify the VNC Server and/or your seeing this Dialog\n"
++ "is due to one of the following:\n"
++ "\n"
++ " - Your requesting to View the Certificate before accepting.\n"
++ "\n"
++ " - The VNC server using a Self-Signed Certificate.\n"
++ " - The VNC server is using a Self-Signed Certificate or a Certificate\n"
++ " Authority not recognized by your Web Browser or Java Plugin runtime.\n"
++ "\n"
++ " - The use of an Apache SSL portal scheme employing CONNECT proxying AND\n"
++ " the Apache Web server has a certificate *different* from the VNC server's.\n"
++ "\n"
++ " - The VNC server using a Certificate Authority not recognized by your\n"
++ " Browser or Java Plugin runtime.\n"
++ " - No previously accepted Certificate (via Web Broswer/Java Plugin) could be\n"
++ " obtained by this applet to compare the VNC Server Certificate against.\n"
++ "\n"
++ " - The use of an Apache SSL portal employing CONNECT proxying and the\n"
++ " Apache web server has a certificate different from the VNC server's. \n"
++ " - The VNC Server's Certificate does not match the one specified in the\n"
++ " supplied 'serverCert' Java Applet Parameter.\n"
++ "\n"
++ " - A Man-In-The-Middle attack impersonating as the VNC server you wish\n"
++ " - A Man-In-The-Middle attack impersonating as the VNC server that you wish\n"
++ " to connect to. (Wouldn't that be exciting!!)\n"
++ "\n"
++ "By safely copying the VNC server's Certificate (or using a common\n"
++ "Certificate Authority certificate) you can configure your Web Browser or\n"
++ "Java Plugin to automatically authenticate this Server.\n"
++ "By safely copying the VNC server's Certificate (or using a common Certificate\n"
++ "Authority certificate) you can configure your Web Browser and Java Plugin to\n"
++ "automatically authenticate this VNC Server.\n"
++ "\n"
++ "If you do so, then you will only have to click \"Yes\" when this VNC\n"
++ "Viewer applet asks you whether to trust your Browser/Java Plugin's\n"
++ "acceptance of the certificate. (except for the Apache portal case above.)\n"
++ "If you do so, then you will only have to click \"Yes\" when this VNC Viewer\n"
++ "applet asks you whether to trust your Browser/Java Plugin's acceptance of the\n"
++ "certificate (except for the Apache portal case above where they don't match.)\n"
++ "\n"
++ "You can also set the applet parameter 'trustUrlVncCert=yes' to automatically\n"
++ "accept certificates already accepted/trusted by your Web Browser/Java Plugin,\n"
++ "and thereby see no dialog from this VNC Viewer applet.\n"
+;
+
+ /* the accept / do-not-accept radio buttons: */
......@@ -3781,7 +4127,7 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+ label.setFont(new Font("Helvetica", Font.BOLD, 16));
+
+ /* textarea in the middle */
+ textarea = new TextArea(text, 36, 64,
+ textarea = new TextArea(text, 38, 64,
+ TextArea.SCROLLBARS_VERTICAL_ONLY);
+ textarea.setEditable(false);
+
......@@ -4133,22 +4479,19 @@ diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java
+m += "\n";
+m += "on its own.\n";
+m += "\n";
+m += "However, it has noticed that your Web Browser or Java VM Plugin\n";
+m += "However, it has noticed that your Web Browser and/or Java VM Plugin\n";
+m += "has previously accepted the same certificate. You may have set\n";
+m += "this up permanently or just for this session, or the server\n";
+m += "certificate was signed by a CA cert that your Web Browser or\n";
+m += "Java VM Plugin has.\n";
+m += "\n";
+m += "If the VNC Server connection times out while you are reading this\n";
+m += "dialog, then restart the connection and try again.\n";
+m += "\n";
+m += "Should this VNC Viewer applet now connect to the above VNC server?\n";
+m += "\n";
+
+// String m = "\nShould this VNC Viewer applet use your Browser/JVM certs to\n";
+// m += "authenticate the VNC Server:\n";
+// m += "\n " + hostport + "\n\n " + vncServer + "\n\n";
+// m += "(NOTE: this *includes* any certs you have Just Now accepted in a\n";
+// m += "dialog box with your Web Browser or Java Applet Plugin)\n\n";
+
+ TextArea textarea = new TextArea(m, 20, 64,
+ TextArea textarea = new TextArea(m, 22, 64,
+ TextArea.SCROLLBARS_VERTICAL_ONLY);
+ textarea.setEditable(false);
+ yes = new Button("Yes");
......@@ -4483,8 +4826,16 @@ diff -Naur JavaViewer.orig/VncCanvas.java JavaViewer/VncCanvas.java
result = 0; // Transparent pixel
diff -Naur JavaViewer.orig/VncViewer.java JavaViewer/VncViewer.java
--- JavaViewer.orig/VncViewer.java 2006-05-24 15:14:40.000000000 -0400
+++ JavaViewer/VncViewer.java 2010-02-22 21:58:51.000000000 -0500
@@ -80,11 +80,11 @@
+++ JavaViewer/VncViewer.java 2010-03-20 19:50:16.000000000 -0400
@@ -41,6 +41,7 @@
import java.io.*;
import java.net.*;
import javax.swing.*;
+import java.util.Date;
public class VncViewer extends java.applet.Applet
implements java.lang.Runnable, WindowListener {
@@ -80,11 +81,11 @@
GridBagLayout gridbag;
ButtonPanel buttonPanel;
AuthPanel authenticator;
......@@ -4498,7 +4849,7 @@ diff -Naur JavaViewer.orig/VncViewer.java JavaViewer/VncViewer.java
// Control session recording.
Object recordingSync;
@@ -96,7 +96,7 @@
@@ -96,7 +97,7 @@
// Variables read from parameter values.
String host;
......@@ -4507,7 +4858,7 @@ diff -Naur JavaViewer.orig/VncViewer.java JavaViewer/VncViewer.java
String passwordParam;
String encPasswordParam;
boolean showControls;
@@ -115,28 +115,71 @@
@@ -115,28 +116,72 @@
int i;
// mslogon support 2 end
......@@ -4518,6 +4869,7 @@ diff -Naur JavaViewer.orig/VncViewer.java JavaViewer/VncViewer.java
+String urlPrefix;
+String httpsPort;
+String oneTimeKey;
+String serverCert;
+String ftpDropDown;
+String proxyHost;
+String proxyPort;
......@@ -4585,7 +4937,7 @@ diff -Naur JavaViewer.orig/VncViewer.java JavaViewer/VncViewer.java
// authenticator = new AuthPanel(false); // mslogon support : go to connectAndAuthenticate()
if (RecordingFrame.checkSecurity())
rec = new RecordingFrame(this);
@@ -147,10 +190,11 @@
@@ -147,10 +192,11 @@
cursorUpdatesDef = null;
eightBitColorsDef = null;
......@@ -4599,7 +4951,7 @@ diff -Naur JavaViewer.orig/VncViewer.java JavaViewer/VncViewer.java
rfbThread = new Thread(this);
rfbThread.start();
}
@@ -186,6 +230,30 @@
@@ -186,6 +232,30 @@
gbc.weightx = 1.0;
gbc.weighty = 1.0;
......@@ -4630,7 +4982,7 @@ diff -Naur JavaViewer.orig/VncViewer.java JavaViewer/VncViewer.java
// Add ScrollPanel to applet mode
// Create a panel which itself is resizeable and can hold
@@ -286,6 +354,24 @@
@@ -286,6 +356,24 @@
void connectAndAuthenticate() throws Exception {
......@@ -4655,7 +5007,7 @@ diff -Naur JavaViewer.orig/VncViewer.java JavaViewer/VncViewer.java
// If "ENCPASSWORD" parameter is set, decrypt the password into
// the passwordParam string.
@@ -336,7 +422,22 @@
@@ -336,7 +424,22 @@
//
......@@ -4679,7 +5031,7 @@ diff -Naur JavaViewer.orig/VncViewer.java JavaViewer/VncViewer.java
authenticator = new AuthPanel(mslogon);
@@ -390,6 +491,10 @@
@@ -390,6 +493,10 @@
break;
//mslogon support end
......@@ -4690,7 +5042,7 @@ diff -Naur JavaViewer.orig/VncViewer.java JavaViewer/VncViewer.java
// Retry on authentication failure.
authenticator.retry();
}
@@ -405,9 +510,11 @@
@@ -405,9 +512,11 @@
void prologueDetectAuthProtocol() throws Exception {
......@@ -4704,7 +5056,7 @@ diff -Naur JavaViewer.orig/VncViewer.java JavaViewer/VncViewer.java
System.out.println("RFB server supports protocol version " +
rfb.serverMajor + "." + rfb.serverMinor);
@@ -431,16 +538,36 @@
@@ -431,16 +540,36 @@
boolean tryAuthenticate(String us, String pw) throws Exception {
......@@ -4747,7 +5099,7 @@ diff -Naur JavaViewer.orig/VncViewer.java JavaViewer/VncViewer.java
switch (authScheme) {
@@ -629,6 +756,10 @@
@@ -629,6 +758,10 @@
void doProtocolInitialisation() throws IOException {
......@@ -4758,9 +5110,12 @@ diff -Naur JavaViewer.orig/VncViewer.java JavaViewer/VncViewer.java
rfb.writeClientInit();
rfb.readServerInit();
@@ -775,8 +906,25 @@
@@ -774,9 +907,28 @@
fatalError("HOST parameter not specified");
}
}
+ Date d = new Date();
+ System.out.println("-\nSSL VNC Java Applet starting. " + d);
- String str = readParameter("PORT", true);
- port = Integer.parseInt(str);
......@@ -4786,7 +5141,7 @@ diff -Naur JavaViewer.orig/VncViewer.java JavaViewer/VncViewer.java
if (inAnApplet) {
str = readParameter("Open New Window", false);
@@ -804,6 +952,138 @@
@@ -804,6 +956,143 @@
deferScreenUpdates = readIntParameter("Defer screen updates", 20);
deferCursorUpdates = readIntParameter("Defer cursor updates", 10);
deferUpdateRequests = readIntParameter("Defer update requests", 50);
......@@ -4840,7 +5195,12 @@ diff -Naur JavaViewer.orig/VncViewer.java JavaViewer/VncViewer.java
+
+ oneTimeKey = readParameter("oneTimeKey", false);
+ if (oneTimeKey != null) {
+ System.out.println("oneTimeKey: is set");
+ System.out.println("oneTimeKey is set.");
+ }
+
+ serverCert = readParameter("serverCert", false);
+ if (serverCert != null) {
+ System.out.println("serverCert is set.");
+ }
+
+ forceProxy = false;
......
2010-03-20 Karl Runge <runge@karlrunge.com>
* classes/ssl: Many improvements to Java SSL applet, onetimekey
serverCert param, debugging printout, user dialogs, catch
socket exceptions, autodetect x11vnc for GET=1.
* x11vnc: misc/scripts: desktop.cgi, inet6to4, panner.pl.
X11VNC_HTTPS_DOWNLOAD_WAIT_TIME, -unixpw %xxx documented, and
can run user cmd in UNIXPW_CMD. FD_XDMCP_IF for create script,
autodetect dm on udp6 only. Queries: pointer_x, pointer_y,
pointer_same, pointer_root. Switch on -xkd if keysyms per key >
4 in all cases. daemon mode improvements for connect_switch,
inet6to4, ultravnc_repeater.pl. Dynamic change of -clip do
not create new fb if WxH is unchanged.
2010-02-22 Karl Runge <runge@karlrunge.com>
* classes/ssl: Java SSL applet viewer now works with certificate
chains.
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -908,6 +908,9 @@ static void tree_descend_cursor(int *depth, Window *w, win_str_info_t *winfo) {
*(winfo->res_name) = '\0';
*(winfo->res_class) = '\0';
for (i=0; i < maxtries; i++) {
wins[i] = None;
}
/* some times a window can go away before we get to it */
trapped_xerror = 0;
......
......@@ -518,6 +518,11 @@ void print_help(int mode) {
" -stunnel the ssl classes subdirectory is sought.\n"
"-http_ssl As -http, but force lookup for ssl classes subdir.\n"
"\n"
" Note that for HTTPS, single-port Java applet delivery\n"
" you can set X11VNC_HTTPS_DOWNLOAD_WAIT_TIME to the\n"
" max number of seconds to wait for the applet download\n"
" to finish. The default is 15.\n"
"\n"
"-avahi Use the Avahi/mDNS ZeroConf protocol to advertise\n"
" this VNC server to the local network. (Related terms:\n"
" Rendezvous, Bonjour). Depending on your setup, you\n"
......@@ -965,9 +970,31 @@ void print_help(int mode) {
" the user is allowed, but the option values associated\n"
" with it do apply as normal.\n"
"\n"
" There are also some utilities for testing password\n"
" There are also some utilities for checking passwords\n"
" if [list] starts with the \"%%\" character. See the\n"
" quick_pw() function in the source for details.\n"
" quick_pw() function for more details. Description:\n"
" \"%%-\" or \"%%stdin\" means read one line from stdin.\n"
" \"%%env\" means it is in $UNIXPW env var. A leading\n"
" \"%%/\" or \"%%.\" means read the first line from the\n"
" filename that follows after the %% character. %% by\n"
" itself means prompt for the username and password.\n"
" Otherwise: %%user:pass E.g. -unixpw %%fred:swordfish\n"
" For the other cases user:pass is read from the indicated\n"
" source. If the password is correct 'Y user' is printed\n"
" and the program exit code is 0. If the password is\n"
" incorrect it prints 'N user' and the exit code is 1.\n"
" If there is some other error the exit code is 2.\n"
" This feature enables x11vnc to be a general unix user\n"
" password checking tool; it could be used from scripts\n"
" or other programs. These %% password checks also apply\n"
" to the -unixpw_nis and -unixpw_cmd options.\n"
"\n"
" For the %% password check, if the env. var. UNIXPW_CMD\n"
" is set to a command then it is run as the user (assuming\n"
" the password is correct.) The output of the command is\n"
" not printed, the program or script must manage that by\n"
" some other means. The exit code of x11vnc will depend\n"
" on the exit code of the command that is run.\n"
"\n"
" Use -nounixpw to disable unixpw mode if it was enabled\n"
" earlier in the cmd line (e.g. -svc mode)\n"
......@@ -1072,8 +1099,11 @@ void print_help(int mode) {
" supplied unixpw_cmd to do user switching if desired\n"
" and if it has the permissions to do so.\n"
"\n"
"-find Find the user's display using FINDDISPLAY. This is an\n"
" alias for \"-display WAIT:cmd=FINDDISPLAY\".\n"
"-find Find the user's display using FINDDISPLAY. This\n"
" is an alias for \"-display WAIT:cmd=FINDDISPLAY\".\n"
"\n"
" Note: if a -display occurs later on the command line\n"
" it will override the -find setting.\n"
"\n"
" For this and the next few options see -display WAIT:...\n"
" below for all of the details.\n"
......@@ -1111,6 +1141,9 @@ void print_help(int mode) {
" FINDCREATEDISPLAY method. This is an alias for\n"
" \"-display WAIT:cmd=FINDCREATEDISPLAY-Xvfb\".\n"
"\n"
" Note: if a -display occurs later on the command line\n"
" it will override the -create setting.\n"
"\n"
" SSH NOTE: for both -find and -create you can (should!)\n"
" add the \"-localhost\" option to force SSH tunnel access.\n"
"\n"
......@@ -1128,6 +1161,10 @@ void print_help(int mode) {
" -display WAIT:cmd=FINDCREATEDISPLAY-Xvfb -unixpw -users\n"
" unixpw= -ssl SAVE Also \"-service\".\n"
"\n"
" Note: if a -display, -unixpw, -users, or -ssl occurs\n"
" later on the command line it will override the -svc\n"
" setting.\n"
"\n"
"-svc_xdummy As -svc except Xdummy instead of Xvfb.\n"
"-svc_xvnc As -svc except Xvnc instead of Xvfb.\n"
"-svc_xdummy_xvfb As -svc with Xdummy,Xvfb.\n"
......@@ -1136,6 +1173,10 @@ void print_help(int mode) {
" Alias for -display WAIT:cmd=FINDCREATEDISPLAY-Xvfb.xdmcp\n"
" -unixpw -users unixpw= -ssl SAVE Also \"-xdm_service\".\n"
"\n"
" Note: if a -display, -unixpw, -users, or -ssl occurs\n"
" later on the command line it will override the -xdmsvc\n"
" setting.\n"
"\n"
" To create a session a user will have to first log in\n"
" to the -unixpw dialog and then log in again to the\n"
" XDM/GDM/KDM prompt. Subsequent re-connections will\n"
......@@ -1485,6 +1526,11 @@ void print_help(int mode) {
" X property, that makes FINDDISPLAY only find sessions\n"
" with that tag value.\n"
"\n"
" Set FD_XDMCP_IF to the network interface that the\n"
" display manager is running on; default is 'localhost'\n"
" but you may need to set it to '::1' on some IPv6 only\n"
" systems or misconfigured display managers.\n"
"\n"
" If you want the FINDCREATEDISPLAY session to contact an\n"
" XDMCP login manager (xdm/gdm/kdm) on the same machine,\n"
" then use \"Xvfb.xdmcp\" instead of \"Xvfb\", etc.\n"
......@@ -2147,7 +2193,7 @@ void print_help(int mode) {
" exits.\n"
"\n"
"\n"
"-stunnel [pem] Use the stunnel(8) (www.stunnel.org) to provide an\n"
"-stunnel [pem] Use the stunnel(8) (stunnel.mirt.net) to provide an\n"
" encrypted SSL tunnel between viewers and x11vnc.\n"
"\n"
" This external tunnel method was implemented prior to the\n"
......@@ -4980,6 +5026,10 @@ void print_help(int mode) {
" for root window, use +id for children.\n"
" grab_state get state of pointer and keyboard grab.\n"
" pointer_pos print XQueryPointer x,y cursor position.\n"
" pointer_x print XQueryPointer x cursor position.\n"
" pointer_y print XQueryPointer y cursor position.\n"
" pointer_same print XQueryPointer ptr on same screen.\n"
" pointer_root print XQueryPointer curr ptr rootwin.\n"
" mouse_x print x11vnc's idea of cursor position.\n"
" mouse_y print x11vnc's idea of cursor position.\n"
" noop do nothing.\n"
......@@ -5278,18 +5328,18 @@ void print_help(int mode) {
" http_url auth xauth users rootshift clipshift scale_str\n"
" scaled_x scaled_y scale_numer scale_denom scale_fac_x\n"
" scale_fac_y scaling_blend scaling_nomult4 scaling_pad\n"
" scaling_interpolate inetd privremote unsafe safer\n"
" nocmds passwdfile unixpw unixpw_nis unixpw_list ssl\n"
" ssl_pem sslverify stunnel stunnel_pem https httpsredir\n"
" usepw using_shm logfile o flag rmflag rc norc h help\n"
" V version lastmod bg sigpipe threads readrate netrate\n"
" netlatency pipeinput clients client_count pid ext_xtest\n"
" ext_xtrap ext_xrecord ext_xkb ext_xshm ext_xinerama\n"
" ext_overlay ext_xfixes ext_xdamage ext_xrandr rootwin\n"
" num_buttons button_mask mouse_x mouse_y grab_state\n"
" pointer_pos bpp depth indexed_color dpy_x dpy_y wdpy_x\n"
" wdpy_y off_x off_y cdpy_x cdpy_y coff_x coff_y rfbauth\n"
" passwd viewpasswd\n"
" scaling_interpolate inetd privremote unsafe safer nocmds\n"
" passwdfile unixpw unixpw_nis unixpw_list ssl ssl_pem\n"
" sslverify stunnel stunnel_pem https httpsredir usepw\n"
" using_shm logfile o flag rmflag rc norc h help V version\n"
" lastmod bg sigpipe threads readrate netrate netlatency\n"
" pipeinput clients client_count pid ext_xtest ext_xtrap\n"
" ext_xrecord ext_xkb ext_xshm ext_xinerama ext_overlay\n"
" ext_xfixes ext_xdamage ext_xrandr rootwin num_buttons\n"
" button_mask mouse_x mouse_y grab_state pointer_pos\n"
" pointer_x pointer_y pointer_same pointer_root bpp depth\n"
" indexed_color dpy_x dpy_y wdpy_x wdpy_y off_x off_y\n"
" cdpy_x cdpy_y coff_x coff_y rfbauth passwd viewpasswd\n"
"\n"
"-QD variable Just like -query variable, but returns the default\n"
" value for that parameter (no running x11vnc server\n"
......
......@@ -1114,7 +1114,8 @@ void switch_to_xkb_if_better(void) {
n = k;
XFree_wr(keymap);
if (missing_noxkb == 0 && syms_gt_4 >= 8) {
if (missing_noxkb == 0 && syms_per_keycode > 4 && syms_gt_4 >= 0) {
/* we used to have syms_gt_4 >= 8, now always on. */
if (! raw_fb_str) {
rfbLog("\n");
rfbLog("XKEYBOARD: number of keysyms per keycode %d is greater\n", syms_per_keycode);
......@@ -1123,6 +1124,7 @@ void switch_to_xkb_if_better(void) {
rfbLog(" If this makes the key mapping worse you can\n");
rfbLog(" disable it with the \"-noxkb\" option.\n");
rfbLog(" Also, remember \"-remap DEAD\" for accenting characters.\n");
rfbLog("\n");
}
use_xkb_modtweak = 1;
......@@ -1135,6 +1137,7 @@ void switch_to_xkb_if_better(void) {
rfbLog(" Not automatically switching to -xkb mode.\n");
rfbLog(" If some keys still cannot be typed, try using -xkb.\n");
rfbLog(" Also, remember \"-remap DEAD\" for accenting characters.\n");
rfbLog("\n");
}
return;
}
......@@ -1217,6 +1220,7 @@ void switch_to_xkb_if_better(void) {
rfbLog(" Also, remember \"-remap DEAD\" for accenting"
" characters.\n");
}
rfbLog("\n");
}
/* sets up all the keymapping info via Xkb API */
......
SUBDIRS = turbovnc
DIST_SUBDIRS = turbovnc
EXTRA_DIST=README blockdpy.c dtVncPopup rx11vnc rx11vnc.pl shm_clear ranfb.pl slide.pl vcinject.pl x11vnc_loop Xdummy ultravnc_repeater.pl connect_switch
EXTRA_DIST=README blockdpy.c dtVncPopup rx11vnc rx11vnc.pl shm_clear ranfb.pl slide.pl vcinject.pl x11vnc_loop Xdummy ultravnc_repeater.pl connect_switch panner.pl desktop.cgi inet6to4
......@@ -33,3 +33,14 @@ Misc. scripts:
use Xsetup mechanism.
Xdummy An LD_PRELOAD kludge to run the Xorg "dummy" device driver
like Xvfb.
ultravnc_repeater.pl: Unix script to act as UltraVNC repeater proxy.
connect_switch: Share HTTPS, VNC, SSH, etc. through a single port (e.g. 443)
panner.pl Allows a small rectangle to pan around a desktop more or less.
desktop.cgi CGI script for creating multi-user virtual desktops on a
server. Also can do port-redirection to internal machines.
inet6to4 ipv6 to ipv4 relay (i.e. until libvncserver supports ipv6)
#!/usr/bin/perl
#
# Copyright (c) 2006-2009 by Karl J. Runge <runge@karlrunge.com>
# Copyright (c) 2006-2010 by Karl J. Runge <runge@karlrunge.com>
#
# connect_switch is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -35,10 +35,14 @@
# because the CONNECT request appears to be forwarded encrypted to
# the remote host and so the SSL dies immediately.
#
# It can also be used to redirect ANY protocol, e.g. SSH, not just VNC.
# See CONNECT_SWITCH_APPLY_VNC_OFFSET=0 to disable VNC 5900 shift.
#
# Note: There is no need to use this script for a non-ssl apache webserver
# port because mod_proxy works fine for doing the switching all inside
# apache (see ProxyRequests and AllowCONNECT parameters).
#
#
# Apache configuration:
#
# The mod_ssl configuration is often in a file named ssl.conf. In the
......@@ -68,6 +72,126 @@
# It is probably a good idea to set $listen_host below to the known
# IP address you want the service to listen on (to avoid localhost where
# apache is listening).
#
####################################################################
# NOTE: For more info on configuration settings, read below for
# all of the CONNECT_SWITCH_* env. var. parameters.
####################################################################
####################################################################
# Allow env vars to also be specified on cmdline:
#
foreach my $arg (@ARGV) {
if ($arg =~ /^(CONNECT_SWITCH.*?)=(.*)$/) {
$ENV{$1} = $2;
}
}
# Set up logging:
#
if (exists $ENV{CONNECT_SWITCH_LOGFILE}) {
close STDOUT;
if (!open(STDOUT, ">>$ENV{CONNECT_SWITCH_LOGFILE}")) {
die "connect_switch: $ENV{CONNECT_SWITCH_LOGFILE} $!\n";
}
close STDERR;
open(STDERR, ">&STDOUT");
}
select(STDERR); $| = 1;
select(STDOUT); $| = 1;
# interrupt handler:
#
my $looppid = '';
my $pidfile = '';
my $listen_sock = ''; # declared here for get_out()
#
sub get_out {
print STDERR "$_[0]:\t$$ looppid=$looppid\n";
close $listen_sock if $listen_sock;
if ($looppid) {
kill 'TERM', $looppid;
fsleep(0.2);
}
unlink $pidfile if $pidfile;
exit 0;
}
$SIG{INT} = \&get_out;
$SIG{TERM} = \&get_out;
# pidfile:
#
sub open_pidfile {
if (exists $ENV{CONNECT_SWITCH_PIDFILE}) {
my $pf = $ENV{CONNECT_SWITCH_PIDFILE};
if (open(PID, ">$pf")) {
print PID "$$\n";
close PID;
$pidfile = $pf;
} else {
print STDERR "could not open pidfile: $pf - $! - continuing...\n";
}
delete $ENV{CONNECT_SWITCH_PIDFILE};
}
}
####################################################################
# Set CONNECT_SWITCH_LOOP=1 to have this script create an outer loop
# restarting itself if it ever exits. Set CONNECT_SWITCH_LOOP=BG to
# do this in the background as a daemon.
if (exists $ENV{CONNECT_SWITCH_LOOP}) {
my $csl = $ENV{CONNECT_SWITCH_LOOP};
if ($csl ne 'BG' && $csl ne '1') {
die "connect_switch: invalid CONNECT_SWITCH_LOOP.\n";
}
if ($csl eq 'BG') {
# go into bg as "daemon":
setpgrp(0, 0);
my $pid = fork();
if (! defined $pid) {
die "connect_switch: $!\n";
} elsif ($pid) {
wait;
exit 0;
}
if (fork) {
exit 0;
}
setpgrp(0, 0);
close STDIN;
if (! $ENV{CONNECT_SWITCH_LOGFILE}) {
close STDOUT;
close STDERR;
}
}
delete $ENV{CONNECT_SWITCH_LOOP};
if (exists $ENV{CONNECT_SWITCH_PIDFILE}) {
open_pidfile();
}
print STDERR "connect_switch: starting service at ", scalar(localtime), " master-pid=$$\n";
while (1) {
$looppid = fork;
if (! defined $looppid) {
sleep 10;
} elsif ($looppid) {
wait;
} else {
exec $0;
exit 1;
}
print STDERR "connect_switch: re-starting service at ", scalar(localtime), " master-pid=$$\n";
sleep 1;
}
exit 0;
}
if (exists $ENV{CONNECT_SWITCH_PIDFILE}) {
open_pidfile();
}
############################################################################
......@@ -83,15 +207,29 @@
# CONNECT_SWITCH_VERBOSE
# CONNECT_SWITCH_APPLY_VNC_OFFSET
# CONNECT_SWITCH_VNC_OFFSET
# CONNECT_SWITCH_LISTEN_IPV6
# CONNECT_SWITCH_BUFSIZE
# CONNECT_SWITCH_LOGFILE
# CONNECT_SWITCH_PIDFILE
#
# You can also set these on the cmdline:
# connect_switch CONNECT_SWITCH_LISTEN=X CONNECT_SWITCH_ALLOW_FILE=Y ...
#
# By default we will use hostname and assume it resolves:
#
my $hostname = `hostname`;
chomp $hostname;
my $listen_host = $hostname;
my $listen_port = 443;
# Let user override listening situation, e.g. multihomed:
#
if (exists $ENV{CONNECT_SWITCH_LISTEN}) {
#
# E.g. CONNECT_SWITCH_LISTEN=192.168.0.32:443
#
($listen_host, $listen_port) = split(/:/, $ENV{CONNECT_SWITCH_LISTEN});
}
......@@ -99,10 +237,21 @@ my $httpd_host = 'localhost';
my $httpd_port = 443;
if (exists $ENV{CONNECT_SWITCH_HTTPD}) {
#
# E.g. CONNECT_SWITCH_HTTPD=127.0.0.1:443
#
($httpd_host, $httpd_port) = split(/:/, $ENV{CONNECT_SWITCH_HTTPD});
}
my $bufsize = 8192;
if (exists $ENV{CONNECT_SWITCH_BUFSIZE}) {
#
# E.g. CONNECT_SWITCH_BUFSIZE=32768
#
$bufsize = $ENV{CONNECT_SWITCH_BUFSIZE};
}
############################################################################
# You can/should override the host/port settings here:
#
......@@ -113,6 +262,9 @@ if (exists $ENV{CONNECT_SWITCH_HTTPD}) {
# You must set the allowed host:port CONNECT redirection list.
# Only these host:port pairs will be redirected to.
# Port ranges are allowed too: host:5900-5930.
# If there is one entry named ALL all connections are allow.
# You must supply something, default is deny.
#
my @allowed = qw(
machine1:5915
......@@ -141,6 +293,8 @@ if (exists $ENV{CONNECT_SWITCH_ALLOWED}) {
# fredsbox 15
# rupert 1
# For examply, mine is:
#
my $allow_file = '/dist/apache/2.0/conf/vnc.hosts';
$allow_file = '';
......@@ -158,25 +312,34 @@ my $apply_vnc_offset = 1;
my $vnc_offset = 5900;
if (exists $ENV{CONNECT_SWITCH_APPLY_VNC_OFFSET}) {
#
# E.g. CONNECT_SWITCH_APPLY_VNC_OFFSET=0
#
$apply_vnc_offset = $ENV{CONNECT_SWITCH_APPLY_VNC_OFFSET};
}
if (exists $ENV{CONNECT_SWITCH_VNC_OFFSET}) {
#
# E.g. CONNECT_SWITCH_VNC_OFFSET=6000
#
$vnc_offset = $ENV{CONNECT_SWITCH_VNC_OFFSET};
}
# Set to 1 for more debugging output:
# Set to 1 or higher for more info output:
#
my $verbose = 0;
if (exists $ENV{CONNECT_SWITCH_VERBOSE}) {
#
# E.g. CONNECT_SWITCH_VERBOSE=1
#
$verbose = $ENV{CONNECT_SWITCH_VERBOSE};
}
############################################################################
#===========================================================================
# No need for any changes below here.
#===========================================================================
use IO::Socket::INET;
use strict;
......@@ -186,12 +349,29 @@ my $killpid = 1;
setpgrp(0, 0);
my $listen_sock = IO::Socket::INET->new(
Listen => 10,
LocalAddr => $listen_host,
LocalPort => $listen_port,
Proto => "tcp"
);
if (exists $ENV{CONNECT_SWITCH_LISTEN_IPV6}) {
# note we leave out LocalAddr.
my $cmd = '
use IO::Socket::INET6;
$listen_sock = IO::Socket::INET6->new(
Listen => 10,
LocalPort => $listen_port,
ReuseAddr => 1,
Domain => AF_INET6,
Proto => "tcp"
);
';
eval $cmd;
die "$@\n" if $@;
} else {
$listen_sock = IO::Socket::INET->new(
Listen => 10,
LocalAddr => $listen_host,
LocalPort => $listen_port,
ReuseAddr => 1,
Proto => "tcp"
);
}
if (! $listen_sock) {
die "connect_switch: $!\n";
......@@ -210,7 +390,7 @@ while (1) {
fsleep(0.5);
next;
}
print STDERR "conn: $conn -- ", $client->peerhost(), "\n" if $verbose;
print STDERR "conn: $conn -- ", $client->peerhost(), " at ", scalar(localtime), "\n" if $verbose;
my $pid = fork();
if (! defined $pid) {
......@@ -237,6 +417,10 @@ sub handle_conn {
my @allow = @allowed;
# read allow file. Note we read it for every connection
# to allow the admin to modify it w/o restarting us.
# better way would be to read in parent and check mtime.
#
if ($allow_file && -f $allow_file) {
if (open(ALLOW, "<$allow_file")) {
while (<ALLOW>) {
......@@ -259,6 +443,8 @@ sub handle_conn {
}
}
# Read the first 7 bytes of connection, see if it is 'CONNECT'
#
my $str = '';
my $N = 0;
my $isconn = 1;
......@@ -267,7 +453,7 @@ sub handle_conn {
sysread($client, $b, 1);
$str .= $b;
$N++;
print STDERR "read: '$str'\n" if $verbose;
print STDERR "read: '$str'\n" if $verbose > 1;
my $cstr = substr('CONNECT', 0, $i+1);
if ($str ne $cstr) {
$isconn = 0;
......@@ -276,28 +462,60 @@ sub handle_conn {
}
my $sock = '';
if ($isconn) {
# it is CONNECT, read rest of HTTP header:
#
while ($str !~ /\r\n\r\n/) {
my $b;
sysread($client, $b, 1);
$str .= $b;
}
print STDERR "read: $str\n" if $verbose;
print STDERR "read: $str\n" if $verbose > 1;
# get http version and host:port
#
my $ok = 0;
my $hostport = '';
my $http_vers = '1.0';
if ($str =~ /^CONNECT\s+(\S+)\s+HTTP\/(\S+)/) {
$hostport = $1;
$http_vers = $2;
foreach my $hp (@allow) {
if ($hp eq $hostport) {
$ok = 1;
last;
my ($h, $p) = split(/:/, $hostport);
if ($p =~ /^\d+$/) {
# check allowed host list:
foreach my $hp (@allow) {
if ($hp eq 'ALL') {
$ok = 1;
}
if ($hp eq $hostport) {
$ok = 1;
}
if ($hp =~ /^(.*):(\d+)-(\d+)$/) {
my $ahost = $1;
my $pmin = $2;
my $pmax = $3;
if ($h eq $ahost) {
if ($p >= $pmin && $p <= $pmax) {
$ok = 1;
}
}
}
last if $ok;
}
}
}
my $msg_1 = "HTTP/$http_vers 200 Connection Established\r\n"
. "Proxy-agent: connect_switch v0.2\r\n\r\n";
my $msg_2 = "HTTP/$http_vers 502 Bad Gateway\r\n"
. "Connection: close\r\n\r\n";
if (! $ok) {
# disallowed. drop with message.
#
syswrite($client, $msg_2, length($msg_2));
close $client;
exit 0;
}
......@@ -312,18 +530,20 @@ sub handle_conn {
Proto => "tcp"
);
my $msg;
# send the connect proxy reply:
#
if ($sock) {
$msg = "HTTP/$http_vers 200 Connection Established\r\n"
. "Proxy-agent: connect_switch v0.2\r\n\r\n";
$msg = $msg_1;
} else {
$msg = "HTTP/$http_vers 502 Bad Gateway\r\n"
. "Connection: close\r\n\r\n";
$msg = $msg_2;
}
syswrite($client, $msg, length($msg));
$str = '';
} else {
print STDERR "connecting to: $httpd_host:$httpd_port\n"
if $verbose;
# otherwise, redirect to apache for normal https:
#
print STDERR "connecting to: $httpd_host:$httpd_port\n" if $verbose;
$sock = IO::Socket::INET->new(
PeerAddr => $httpd_host,
PeerPort => $httpd_port,
......@@ -336,6 +556,8 @@ sub handle_conn {
die "connect_switch: $!\n";
}
# get ready for xfer phase:
#
$current_fh1 = $client;
$current_fh2 = $sock;
......@@ -349,6 +571,8 @@ sub handle_conn {
kill 'TERM', $child;
}
} else {
# write those first bytes if not CONNECT:
#
if ($str ne '' && $N > 0) {
syswrite($sock, $str, $N);
}
......@@ -358,9 +582,9 @@ sub handle_conn {
kill 'TERM', $parent;
}
}
if ($verbose) {
if ($verbose > 1) {
my $dt = time() - $start;
print STDERR "dt\[$$]: $dt\n";
print STDERR "duration\[$$]: $dt seconds. ", scalar(localtime), "\n";
}
exit 0;
}
......@@ -380,7 +604,7 @@ sub xfer {
while (! $nf) {
$nf = select($ROUT=$RIN, undef, undef, undef);
}
my $len = sysread($in, $buf, 8192);
my $len = sysread($in, $buf, $bufsize);
if (! defined($len)) {
next if $! =~ /^Interrupted/;
print STDERR "connect_switch\[$lab/$conn/$$]: $!\n";
......@@ -392,7 +616,7 @@ sub xfer {
}
if (0) {
# verbose debugging of data:
# very verbose debugging of data:
syswrite(STDERR , "\n$lab: ", 6);
syswrite(STDERR , $buf, $len);
}
......
#!/usr/bin/perl
#
# desktop.cgi
#
# An example cgi script to provide multi-user web access to x11vnc
# desktops. This script should/must be served by an HTTPS webserver,
# otherwise the unix and vnc passwords are sent over the network
# unencrypted (see below to disable)
#
# Note that the x11vnc -create virtual desktop service used below requires
# that you install the 'Xvfb' program.
#
# You should put this script in, say, a cgi-bin directory.
#
# You will *also* need to copy the x11vnc classes/ssl/UltraViewerSSL.jar
# file to the document root: /UltraViewerSSL.jar (or change the html
# at bottom.)
#
# Each x11vnc server created for a login will listen on its own port (see
# below for port selection schemes.) Your firewall must let in these ports.
# It is difficult and not as reliable to do all of this through a single port;
# however, see the fixed port scheme find_free_port = 'fixed:5900' below.
#
# Note there are two SSL certificates involved that the user may be
# asked to inspect: apache's SSL cert and x11vnc's SSL cert. This may
# confuse the user.
#
# This script provides one example on how to provide the service. You can
# customize to meet your needs, e.g. switch to php, newer modules,
# different authentication, SQL database, etc. If you plan to use it
# in production, please examine all security aspects of it carefully;
# read the comments in the script for more info.
#
# More information and background:
#
# http://www.karlrunge.com/x11vnc/faq.html#faq-xvfb
# http://www.karlrunge.com/x11vnc/faq.html#faq-ssl-tunnel-viewers
# http://www.karlrunge.com/x11vnc/faq.html#faq-ssl-java-viewer-proxy
# http://www.karlrunge.com/x11vnc/faq.html#faq-ssl-portal
# http://www.karlrunge.com/x11vnc/faq.html#faq-unix-passwords
# http://www.karlrunge.com/x11vnc/faq.html#faq-userlogin
#-------------------------------------------------------------------------
# Copyright (c) 2010 by Karl J. Runge <runge@karlrunge.com>
#
# desktop.cgi 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.
#
# desktop.cgi 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 desktop.cgi; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
# or see <http://www.gnu.org/licenses/>.
#-------------------------------------------------------------------------
use strict;
use IO::Socket::INET;
# TCP Ports:
#
# Set find_free_port to 1 (or the other modes described below) to
# autoselect a free port to use. The default is to use a fixed port
# based on the userid.
#
my $find_free_port = 0;
#
# Or specify a port range:
#
#$find_free_port = '7000-8000';
#
# Or indicate to use a kludge to try to do everything through a SINGLE
# port. To try to avoid contention on the port, simultaneous instances
# of this script attempt to 'take turns' using it.
#
#$find_free_port = 'fixed:5900';
# Port redirection mode:
#
# This is to allow port redirection mode: username@host:port If username
# is valid, there will be a port redirection to internal machine
# host:port. Presumably there is already an SSL enabled and password
# protected VNC server running there. We don't start that server.
# See the next setting for an allowed hosts file. The default for port
# redirection is off.
#
my $enable_port_redirection = 0;
# A file with allowed port redirections. The empty string '' (the
# default) means all host:port redirections would be allowed.
#
# Format of the file: A list of 'user@host:port' or 'host:port'
# entries, one per line. Port ranges, e.g. host:n-m are also accepted.
#
# Leading and trailing whitespace is trimmed off each line. Blank lines
# and comment lines starting with '#' are skipped. A line consisting of
# 'ALL' matches everything. If no match can be found or the file cannot
# be opened the connection is dropped.
#
my $port_redirection_allowed_hosts = '';
# Set to 0 to have the java applet html set the parameter
# trustUrlVncCert=no, i.e. the applet will not automatically accept an
# SSL cert already accepted by an HTTPS URL. See print_applet_html()
# below for more info.
#
my $trustUrlVncCert = 1;
# Comment this out if you don't want PATH modified:
#
$ENV{PATH} = "/usr/bin:bin:$ENV{PATH}";
# For the next two settings, note that most users will be confused that
# geometry and session are ignored when they are returning to their
# existing desktop session (x11vnc FINDDISPLAY action.)
# Used below if user did not specify preferred geometry and color depth:
#
my $default_geometry = '1024x768x24';
# Set this to the list of x11vnc -create sessions types to show a session
# dropdown for the user to select from.
#
my $session_types = '';
#
# example:
#$session_types = 'gnome kde xfce lxde wmaker enlightenment mwm twm failsafe';
# Set this to 1 to enable user setting a unique tag for each one
# of his desktops and so can have multiple ones simultaneously and
# select which one he wants. For now we just hack this onto geometry
# 1024x768x24:my_2nd_desktop but ultimately there should be a form entry
# for it. Search for enable_unique_tags for more info:
#
my $enable_unique_tags = 0;
my $unique_tag = '';
# You can set some extra x11vnc cmdline options here:
#
my $x11vnc_extra_opts = '';
# Path to x11vnc program:
#
my $x11vnc = '/usr/bin/x11vnc';
if (`uname -n` =~ /haystack/) {
# for my testing:
if (-f "/home/runge/dtcgi.test") {
eval `cat /home/runge/dtcgi.test`;
}
}
# http header:
#
print STDOUT "Content-Type: text/html\r\n\r\n";
# Require HTTPS so that unix and vnc passwords are not sent in clear text
# (perhaps it is too late...) Disable HTTPS at your own risk.
#
if ($ENV{HTTPS} !~ /^on$/i) {
bye("HTTPS must be used (to encrypt passwords)");
}
# Read request:
#
my $request;
if ($ENV{'REQUEST_METHOD'} eq "POST") {
read(STDIN, $request, $ENV{'CONTENT_LENGTH'});
} elsif ($ENV{'REQUEST_METHOD'} eq "GET" ) {
$request = $ENV{'QUERY_STRING'};
} else {
$request = $ARGV[0];
}
my %request = url_decode(split(/[&=]/, $request));
# Experiment for FD_TAG x11vnc feature for multiple desktops:
#
# we hide it in geometry:tag for now:
#
if ($enable_unique_tags && $request{geometry} =~ /^(.*):(\w+)$/) {
$request{geometry} = $1;
$unique_tag = $2;
}
# Check/set geometry and session:
#
if (!exists $request{geometry} || $request{geometry} !~ /^[x\d]+$/) {
# default geometry and depth:
$request{geometry} = $default_geometry;
}
if (!exists $request{session} || $request{session} =~ /^\s*$/) {
$request{session} = '';
}
# String for the login form:
#
my $login_str = <<"END";
<title>x11vnc web access</title>
<h3>x11vnc web access</h3>
<form action="$ENV{REQUEST_URI}" method="post">
<table border="0">
<tr><td colspan=2><h2>Login</h2></td></tr>
<tr><td>Username:</td><td>
<input type="text" name="username" maxlength="40" value="$request{username}">
</td></tr>
<tr><td>Password:</td><td>
<input type="password" name="password" maxlength="50">
</td></tr>
<tr><td>Geometry:</td><td>
<input type="text" name="geometry" maxlength="40" value="$request{geometry}">
</td></tr>
<!-- session -->
<tr><td colspan="2" align="right">
<input type="submit" name="submit" value="Login">
</td></tr>
</table>
</form>
END
# Set up user selected desktop session list, if enabled:
#
my %sessions;
if ($session_types ne '') {
my $str = "<tr><td>Session:</td><td>\n<select name=session>";
$str .= "<option value=none>select</option>";
foreach my $sess (split(' ', $session_types)) {
next if $sess =~ /^\s*$/;
next if $sess !~ /^\w+$/; # alphanumeric
$sessions{$sess} = 1;
$str .= "<option value=$sess>$sess</option>";
}
$str .= "</select>\n</td></tr>";
# This forces $request{session} to be a valid one:
#
if (! exists $sessions{$request{session}}) {
$request{session} = 'none';
}
# Insert into login_str:
#
my $r = $request{session};
$str =~ s/option value=\Q$r\E/option selected value=$r/;
$login_str =~ s/<!-- session -->/$str/;
}
# If no username or password, show login form:
#
if (!$request{username} && !$request{password}) {
bye($login_str);
} elsif (!$request{username}) {
bye("No Username.<p>$login_str");
} elsif (!$request{password}) {
bye("No Password.<p>$login_str");
}
# Some shorthand names:
#
my $username = $request{username};
my $password = $request{password};
my $geometry = $request{geometry};
my $session = $request{session};
# If port redirection is enabled, split username@host:port
#
my $redirect_host = '';
my $current_fh1 = '';
my $current_fh2 = '';
if ($enable_port_redirection) {
($username, $redirect_host) = split(/@/, $username, 2);
if ($redirect_host ne '') {
# will exit if the redirection is not allowed:
check_redirect_host();
}
}
# Require username to be alphanumeric + '-' + '_':
# (one may want to add '.' as well)
#
if ($username !~ /^\w[-\w]*$/) {
bye("Invalid Username.<p>$login_str");
}
# Get the userid number, we may use it as his VNC display port; this
# also checks if the username exists:
#
my $uid = `/usr/bin/id -u '$username'`;
chomp $uid;
if ($? != 0 || $uid !~ /^\d+$/) {
bye("Invalid Username.<p>$login_str");
}
# Use x11vnc trick to check if the unix password is valid:
#
if (!open(X11VNC, "| $x11vnc -unixpw \%stdin > /dev/null")) {
bye("Internal Error #1");
}
print X11VNC "$username:$password\n";
if (!close X11VNC) {
# x11vnc returns non-zero for invalid username+password:
bye("Invalid Password.<p>$login_str");
}
# Initialize random number generator for use below:
#
initialize_random();
# Set vnc port:
#
my $vnc_port = 0;
my $fixed_port = 0;
if (! $find_free_port) {
# Fixed port based on userid (we assume it is free):
#
$vnc_port = 7000 + $uid;
} elsif ($find_free_port =~ /^fixed:(\d+)$/) {
#
# Enable the -loopbg method that tries to share a single port:
#
$vnc_port = $1;
$fixed_port = 1;
} else {
# Autoselect a port, either default range (7000-8000) or a user
# supplied range. (note that $find_free_port will now contain
# a socket listening on the found port so that it is held.)
#
$vnc_port = auto_select_port();
}
# Check for crazy port value:
#
if ($vnc_port > 64000 || $vnc_port < 1) {
bye("Internal Error #2 $vnc_port");
}
# If port redirection is enabled and the user selected it via
# username@host:port, we do that right now and then exit.
#
if ($enable_port_redirection && $redirect_host ne '') {
port_redir();
exit 0;
}
# Make a random, onetime vnc password:
#
my $pass = '';
my $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
my @abc = split(//, $chars);
for (my $i = 0; $i < 8; $i++) {
$pass .= $abc[ rand(scalar(@abc)) ];
}
# Use x11vnc trick to switch to user and store vnc pass in the passwdfile.
# Result is $pass is placed in user's $HOME/x11vnc.pw
#
# (This is actually difficult to do without untrusted local users being
# able to see the pass as well, see copy_password_to_user() for details
# on how we try to avoid this.)
#
copy_password_to_user($pass);
# Make a tmp file for x11vnc launcher script:
#
my $tmpfile = `/bin/mktemp /tmp/desktop.cgi.XXXXXX`;
chomp $tmpfile;
# Check if the tmpfile is valid:
#
if (! -e $tmpfile || ! -o $tmpfile || -l $tmpfile) {
unlink $tmpfile;
bye("Internal Error #3");
}
if (!chmod 0644, $tmpfile) {
unlink $tmpfile;
bye("Internal Error #4");
}
if (!open(TMP, ">$tmpfile")) {
unlink $tmpfile;
bye("Internal Error #5");
}
# The x11vnc command. You adjust it to suit your needs.
#
# some ideas: -env FD_PROG=/usr/bin/gnome-session
# -env FD_SESS=kde
# -env FD_TAG=my_2nd_desktop
# -ultrafilexfer
#
# Note that -timeout will cause it to exit if client does not connect
# and -sslonly disables VeNCrypt SSL connections.
# Some settings:
#
my $timeout = 75;
my $extra = '';
if ($fixed_port) {
# settings for fixed port case:
$timeout = 45;
$extra .= " -loopbg100,1";
}
if ($session_types ne '') {
# settings for session selection case:
if (exists $sessions{$session}) {
$extra .= " -env FD_SESS='$session'";
}
}
if ($enable_unique_tags && $unique_tag ne '' && $unique_tag =~ /^\w+$/) {
$extra .= " -env FD_TAG='$unique_tag'";
}
# This md5sum check of the vnc passwd is for extra safety (see
# copy_password_to_user for details.)
#
my $md5sum = '';
system("type md5sum > /dev/null");
if ($? == 0) {
my $md5 = `/bin/mktemp /tmp/desktop.cgi.XXXXXX`;
chomp $md5;
# compute md5sum of password:
if (-o $md5 && open(MD5, "| md5sum > $md5")) {
print MD5 "$pass\n";
close MD5;
if (open(MD5, "<$md5")) {
# read it:
my $line = <MD5>;
close MD5;
my ($s, $t) = split(' ', $line);
if (length($s) >= 32 && $s =~ /^\w+$/) {
# shell code for user to check he has correct passwd:
$md5sum = "if md5sum \$HOME/x11vnc.pw | grep '$s' > /dev/null; then true; else exit 1; fi";
}
}
}
unlink $md5;
}
# write x11vnc command to the tmp file:
#
print TMP <<"END";
#!/bin/sh
export PATH=/usr/bin:/bin:\$PATH
$md5sum
$x11vnc -sigpipe ignore:HUP -nopw -rfbport $vnc_port \\
-passwdfile \$HOME/x11vnc.pw -oa \$HOME/x11vnc.log \\
-create -ssl SAVE -sslonly -env FD_GEOM=$geometry \\
-timeout $timeout $extra $x11vnc_extra_opts \\
>/dev/null 2>/dev/null </dev/null &
sleep 2
exit 0
END
close TMP;
# Now launch x11vnc to switch to user and run the wrapper script:
# (this requires x11vnc 0.9.10 or later.)
#
$ENV{UNIXPW_CMD} = "/bin/sh $tmpfile";
# For the fixed port scheme we try to cooperate via lock file:
#
my $rmlock = '';
#
if ($fixed_port) {
# try to grab the fixed port for the next 90 secs removing stale
# locks older than 60 secs:
#
$rmlock = lock_fixed_port(90, 60);
}
# Start the x11vnc cmd:
#
if (!open(X11VNC, "| $x11vnc -unixpw \%stdin > /dev/null")) {
unlink $tmpfile;
unlink $rmlock if $rmlock;
bye("Internal Error #6");
}
select(X11VNC); $| = 1; select(STDOUT);
# Close any port we held. There is still a gap of time between now
# and when when x11vnc in $tmpfile reopens the port after the password
# authentication. So another instance of this script could accidentally
# think it is free...
#
sleep 1;
close $find_free_port if $find_free_port;
print X11VNC "$username:$password\n";
close X11VNC; # note we ignore return value.
unlink $tmpfile;
if ($rmlock) {
# let our x11vnc proceed a bit before removing lock.
sleep 2;
unlink $rmlock;
}
# Return html for the java applet to connect to x11vnc.
#
print_applet_html();
exit 0;
#################################################################
# Subroutines:
# print the message to client and exit with success.
#
sub bye {
my $msg = shift;
print STDOUT "<html>$msg</html>\n";
exit 0;
}
# decode %xx to character:
#
sub url_decode {
foreach (@_) {
tr/+/ /;
s/%(..)/pack("c",hex($1))/ge;
}
@_;
}
# seed random
#
sub initialize_random {
my $rbytes = '';
if (open(RAN, "</dev/urandom")) {
read(RAN, $rbytes, 8);
} elsif (open(RAN, "</dev/random")) {
read(RAN, $rbytes, 8);
} else {
$rbytes = sprintf("%08d", $$);
}
close RAN;
# set seed:
#
my $seed = join('', unpack("C8", $rbytes));
$seed = substr($seed, -9);
srand($seed);
for (my $i = 0; $i < ($$ % 4096); $i++) {
# Mix it up even a little bit more. There should be
# over 1 billion possible vnc passwords now.
rand();
}
}
# Autoselect a port for vnc. Note that a socket for the found port
# is kept open (and stored in $find_free_port) until we call x11vnc at
# the end.
#
sub auto_select_port {
my $pmin = 7000; # default range.
my $pmax = 8000;
if ($find_free_port =~ /^(\d+)-(\d+)$/) {
# user supplied a range:
$pmin = $1;
$pmax = $2;
if ($pmin > $pmax) {
($pmin, $pmax) = ($pmax, $pmin);
}
} elsif ($find_free_port > 1024) {
# user supplied a starting port:
$pmin = $find_free_port;
$pmax = $pmin + 1000;
}
# Try to add a bit of randomness to the starting port so
# simultaneous instances of this script won't be fooled by the gap
# of time before x11vnc reopens the port (see near the bottom.)
#
my $dp = int(rand(1.0) * 0.25 * ($pmax - $pmin));
if ($pmin + $dp < $pmax - 20) {
$pmin = $pmin + $dp;
}
my $port = 0;
# Now try to find a free one:
#
for (my $p = $pmin; $p <= $pmax; $p++) {
my $sock = IO::Socket::INET->new(
Listen => 1,
LocalPort => $p,
ReuseAddr => 1,
Proto => "tcp"
);
if ($sock) {
# we will keep this open until we call x11vnc:
$find_free_port = $sock;
$port = $p;
last;
}
}
return $port;
}
# Since apache typically runs as user 'apache', 'nobody', etc, and not
# as root it is tricky for us to copy the pass string to a file owned by
# the user without some other untrusted local user being able to learn
# the password (e.g. via reading a file or watching ps.) Note that with
# the x11vnc -unixpw trick we unfortunately can't use a pipe because
# the user command is run in its own tty.
#
# The best way would be a sudo action or a special setuid program for
# copying. So consider using that and thereby simplify this function.
#
# Short of a special program doing this, we use a fifo so ONLY ONE
# process can read the password. If the untrusted local user reads it,
# then the logging-in user's x11vnc won't get it. The login and x11vnc
# will fail, but the untrusted user won't gain access to the logging-in
# user's desktop.
#
# So here we start long, tedious work carefully managing the fifo.
#
sub copy_password_to_user {
my $pass = shift;
my $use_fifo = '';
# Find a command to make a fifo:
#
system("type mkfifo > /dev/null");
if ($? == 0) {
$use_fifo = 'mkfifo %s';
} else {
system("type mknod > /dev/null");
if ($? == 0) {
$use_fifo = 'mknod %s p';
}
}
# Create the filename for our fifo:
#
my $fifo = `/bin/mktemp /tmp/desktop.cgi.XXXXXX`;
chomp $fifo;
if (! -e $fifo || ! -o $fifo || -l $fifo) {
unlink $fifo;
bye("Internal Error #7");
}
# Make the fifo:
#
if ($use_fifo) {
$use_fifo = sprintf($use_fifo, $fifo);
# there is a small race here:
system("umask 077; rm -f $fifo; $use_fifo; chmod 600 $fifo");
if (!chmod 0600, $fifo) {
# we chmod once more..
unlink $fifo;
bye("Internal Error #8");
}
if (! -o $fifo || ! -p $fifo || -l $fifo) {
# but we get out if not owned by us anymore:
unlink $fifo;
bye("Internal Error #9");
}
}
# Build cmd for user to read our fifo:
#
my $upw = '$HOME/x11vnc.pw';
$ENV{UNIXPW_CMD} = "touch $upw; chmod 600 $upw; cat $fifo > $upw";
# Start it:
#
if (!open(X11VNC, "| $x11vnc -unixpw \%stdin > /dev/null")) {
unlink $fifo;
bye("Internal Error #10");
}
select(X11VNC); $| = 1; select(STDOUT);
if (! $use_fifo) {
# regular file, we need to write it now.
if (!open(FIFO, ">$fifo")) {
close X11VNC;
unlink $fifo;
bye("Internal Error #11");
}
print FIFO "$pass\n";
close FIFO;
}
# open fifo up for reading.
# (this means the bad guy can read it too.)
#
if (!chmod 0644, $fifo) {
unlink $fifo;
bye("Internal Error #12");
}
# send the user's passwd now:
#
print X11VNC "$username:$password\n";
if ($use_fifo) {
# wait a bit for the cat $fifo to start, reader will block.
sleep 1;
if (!open(FIFO, ">$fifo")) {
close X11VNC;
unlink $fifo;
bye("Internal Error #13");
}
# here it goes:
print FIFO "$pass\n";
close FIFO;
}
close X11VNC; # note we ignore return value.
fsleep(0.5);
#print STDERR `ls -l $fifo ~$username/x11vnc.pw`;
unlink $fifo;
# Done!
}
# For fixed, single port mode. Try to open and lock the port before
# proceeding.
#
sub lock_fixed_port {
my ($t_max, $t_age) = @_;
# lock file name:
#
my $lock = '/tmp/desktop.cgi.lock';
my $remove = '';
my $t = 0;
my $sock = '';
while ($t < $t_max) {
if (-e $lock) {
# clean out stale locks if possible:
if (! -l $lock) {
unlink $lock;
} else {
my ($pid, $time) = split(/:/, readlink($lock));
if (! -d "/proc/$pid") {
unlink $lock;
}
if (time() > $time + $t_age) {
unlink $lock;
}
}
}
my $reason = '';
if (-l $lock) {
# someone has locked it.
$reason = 'locked';
} else {
# unlocked, try to listen on port:
$sock = IO::Socket::INET->new(
Listen => 1,
LocalPort => $vnc_port,
ReuseAddr => 1,
Proto => "tcp"
);
if ($sock) {
# we got it, now try to lock:
my $str = "$$:" . time();
if (symlink($str, $lock)) {
$remove = $lock;
$find_free_port = $sock;
last;
}
# wow, we didn't lock it...
$reason = "symlink failed: $!";
close $sock;
} else {
$reason = "listen failed: $!";
}
}
# sleep a bit and then try again:
#
print STDERR "$$ failed to get fixed port $vnc_port for $username at $t ($reason)\n";
$sock = '';
$t += 5;
sleep 5;
}
if (! $sock) {
bye("Failed to lock fixed TCP port. Try again a bit later.<p>$login_str");
}
print STDERR "$$ got fixed port $vnc_port for $username at $t\n";
# Return the file to remove, if any:
#
return $remove;
}
# Return html for the java applet to connect to x11vnc.
#
# N.B. Please examine the applet params, e.g. trustUrlVncCert=yes to
# see if you agree with them. See x11vnc classes/ssl/README for all
# parameters.
#
# Note how we do not take extreme care to authenticate the server to
# the client applet (but note that trustUrlVncCert=yes is better than
# trustAllVncCerts=yes) One can tighten all of this up at the expense
# of extra certificate dialogs (assuming the user bothers to check...)
#
# This assumes /UltraViewerSSL.jar is at document root; you need to put
# it there.
#
sub print_applet_html {
my ($W, $H, $D) = split(/x/, $geometry);
$W = 640; # make it smaller since we 'Open New Window' below anyway.
$H = 480;
my $tUVC = ($trustUrlVncCert ? 'yes' : 'no');
my $str = <<"END";
<html>
<TITLE>
x11vnc desktop ($uid/$vnc_port)
</TITLE>
<APPLET CODE=VncViewer.class ARCHIVE=/UltraViewerSSL.jar WIDTH=$W HEIGHT=$H>
<param name=PORT value=$vnc_port>
<param name=VNCSERVERPORT value=$vnc_port>
<param name=PASSWORD value=$pass>
<param name=trustUrlVncCert value=$tUVC>
<param name="Open New Window" value=yes>
<param name="Offer Relogin" value=no>
<param name="ignoreMSLogonCheck" value=yes>
<param name="delayAuthPanel" value=yes>
<!-- extra -->
</APPLET>
<br>
<a href="$ENV{REQUEST_URI}">Login page</a><br>
<a href=http://www.karlrunge.com/x11vnc>x11vnc website</a>
</html>
END
if ($enable_port_redirection && $redirect_host ne '') {
$str =~ s/name=PASSWORD value=.*>/name=NOT_USED value=yes>/;
#$str =~ s/<!-- extra -->/<!-- extra -->\n<param name="ignoreProxy" value=yes>/;
}
print $str;
}
##########################################################################
# The following subroutines are for port redirection only, which is
# disabled by default ($enable_port_redirection == 0)
#
sub port_redir {
# To aid in avoiding zombies:
#
setpgrp(0, 0);
# For the fixed port scheme we try to cooperate via lock file:
#
my $rmlock = '';
#
if ($fixed_port) {
# try to grab the fixed port for the next 90 secs removing
# stale locks older than 60 secs:
#
$rmlock = lock_fixed_port(90, 60);
} elsif ($find_free_port eq '0') {
$find_free_port = IO::Socket::INET->new(
Listen => 1,
LocalPort => $vnc_port,
ReuseAddr => 1,
Proto => "tcp"
);
}
# In all cases, at this point $find_free_port is the listening
# socket.
# fork a helper process to do the port redir:
#
# Actually we need to spawn 4(!) of them in case the proxy check
# /check.https.proxy.connection (it is by default) and the other
# test connections. Spawn one for each expected connection, for
# whatever applet parameter usage mode you set up.
#
for (my $n = 1; $n <= 4; $n++) {
my $pid = fork();
if (! defined $pid) {
bye("Internal Error #14");
} elsif ($pid) {
wait;
} else {
if (fork) {
exit 0;
}
setpgrp(0, 0);
handle_conn();
exit 0;
}
}
# We now close the listening socket:
#
close $find_free_port;
if ($rmlock) {
# let our process proceed a bit before removing lock.
sleep 1;
unlink $rmlock;
}
# Now send html to the browser so it can connect:
#
print_applet_html();
exit 0;
}
# This checks the validity of a username@host:port for the port
# redirection mode. Finishes and exits if it is invalid.
#
sub check_redirect_host {
# First check that the host:port string is valid:
#
if ($redirect_host !~ /^\w[-\w\.]*:\d+$/) {
bye("Invalid Redirect Host:Port.<p>$login_str");
}
# Second, check if the allowed host file permits it:
#
if ($port_redirection_allowed_hosts ne '') {
if (! open(ALLOWED, "<$port_redirection_allowed_hosts")) {
bye("Internal Error #15");
}
my $ok = 0;
while (my $line = <ALLOWED>) {
chomp $line;
# skip blank lines and '#' comments:
next if $line =~ /^\s*$/;
next if $line =~ /^\s*#/;
# trim spaces from ends:
$line =~ s/^\s*//;
$line =~ s/\s*$//;
# collect host:ports in case port range given:
my @items;
if ($line =~ /^(.*):(\d+)-(\d+)$/) {
# port range:
my $host = $1;
my $pmin = $2;
my $pmax = $3;
for (my $p = $pmin; $p <= $pmax; $p++) {
push @items, "$host:$p";
}
} else {
push @items, $line;
}
# now check each item for a match:
foreach my $item (@items) {
if ($item eq 'ALL') {
$ok = 1;
last;
} elsif ($item =~ /@/) {
if ("$username\@$redirect_host" eq $item) {
$ok = 1;
last;
}
} elsif ($redirect_host eq $item) {
$ok = 1;
last;
}
}
# got a match:
last if $ok;
}
close ALLOWED;
if (! $ok) {
bye("Disallowed Redirect Host:Port.<p>$login_str");
}
}
}
# Much of this code is borrowed from 'connect_switch':
#
sub handle_conn {
close STDIN;
close STDOUT;
close STDERR;
$SIG{ALRM} = sub {close $find_free_port; exit 0};
# We only wait 30 secs for the redir case, esp. since
# we need to spawn so many helpers...
#
alarm(30);
my ($client, $ip) = $find_free_port->accept();
alarm(0);
close $find_free_port;
if (!$client) {
exit 1;
}
my ($host, $port) = split(/:/, $redirect_host);
my $sock = IO::Socket::INET->new(
PeerAddr => $host,
PeerPort => $port,
Proto => "tcp"
);
if (! $sock) {
close $client;
exit 1;
}
$current_fh1 = $client;
$current_fh2 = $sock;
$SIG{TERM} = sub {close $current_fh1; close $current_fh2; exit 0};
my $killpid = 1;
my $parent = $$;
if (my $child = fork()) {
xfer($sock, $client, 'S->C');
if ($killpid) {
fsleep(0.5);
kill 'TERM', $child;
}
} else {
xfer($client, $sock, 'C->S');
if ($killpid) {
fsleep(0.75);
kill 'TERM', $parent;
}
}
exit 0;
}
# This does socket data transfer in one direction.
#
sub xfer {
my($in, $out, $lab) = @_;
my ($RIN, $WIN, $EIN, $ROUT);
$RIN = $WIN = $EIN = "";
$ROUT = "";
vec($RIN, fileno($in), 1) = 1;
vec($WIN, fileno($in), 1) = 1;
$EIN = $RIN | $WIN;
my $buf;
while (1) {
my $nf = 0;
while (! $nf) {
$nf = select($ROUT=$RIN, undef, undef, undef);
}
my $len = sysread($in, $buf, 8192);
if (! defined($len)) {
next if $! =~ /^Interrupted/;
last;
} elsif ($len == 0) {
last;
}
my $offset = 0;
my $quit = 0;
while ($len) {
my $written = syswrite($out, $buf, $len, $offset);
if (! defined $written) {
$quit = 1;
last;
}
$len -= $written;
$offset += $written;
}
last if $quit;
}
close($in);
close($out);
}
# Sleep a small amount of time (float)
#
sub fsleep {
my ($time) = @_;
select(undef, undef, undef, $time) if $time;
}
#!/usr/bin/perl
#
# inet6to4: Act as an ipv6-to-ipv4 relay for tcp applications that
# do not support ipv6.
#
# Usage: inet6to4 <ipv6-listen-port> <ipv4-host:port>
# inet6to4 -r <ipv4-listen-port> <ipv6-host:port>
#
# Examples: inet6to4 5900 localhost:5900
# inet6to4 8080 web1:80
# inet6to4 -r 5900 fe80::217:f2ff:fee6:6f5a%eth0:5900
#
# The -r option reverses the direction of translation (e.g. for ipv4
# clients that need to connect to ipv6 servers.) Reversing is the default
# if this script is named 'inet4to6' (e.g. by a symlink.)
#
# Use Ctrl-C to stop this program.
#
# You can also set env. vars INET6TO4_LOOP=1 or INET6TO4_LOOP=BG
# to have an outer loop restarting this program (BG means do that
# in the background), and INET6TO4_LOGFILE for a log file.
# Also set INET6TO4_VERBOSE to verbosity level and INET6TO4_WAITTIME
# and INET6TO4_PIDFILE (see below.)
#
#-------------------------------------------------------------------------
# Copyright (c) 2010 by Karl J. Runge <runge@karlrunge.com>
#
# inet6to4 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.
#
# inet6to4 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 inet6to4; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
# or see <http://www.gnu.org/licenses/>.
#-------------------------------------------------------------------------
# Set up logging:
#
if (exists $ENV{INET6TO4_LOGFILE}) {
close STDOUT;
if (!open(STDOUT, ">>$ENV{INET6TO4_LOGFILE}")) {
die "inet6to4: $ENV{INET6TO4_LOGFILE} $!\n";
}
close STDERR;
open(STDERR, ">&STDOUT");
}
select(STDERR); $| = 1;
select(STDOUT); $| = 1;
# interrupt handler:
#
my $looppid = '';
my $pidfile = '';
my $listen_sock = ''; # declared here for get_out()
#
sub get_out {
print STDERR "$_[0]:\t$$ looppid=$looppid\n";
close $listen_sock if $listen_sock;
if ($looppid) {
kill 'TERM', $looppid;
fsleep(0.2);
}
unlink $pidfile if $pidfile;
exit 0;
}
$SIG{INT} = \&get_out;
$SIG{TERM} = \&get_out;
# pidfile:
#
sub open_pidfile {
if (exists $ENV{INET6TO4_PIDFILE}) {
my $pf = $ENV{INET6TO4_PIDFILE};
if (open(PID, ">$pf")) {
print PID "$$\n";
close PID;
$pidfile = $pf;
} else {
print STDERR "could not open pidfile: $pf - $! - continuing...\n";
}
delete $ENV{INET6TO4_PIDFILE};
}
}
####################################################################
# Set INET6TO4_LOOP=1 to have this script create an outer loop
# restarting itself if it ever exits. Set INET6TO4_LOOP=BG to
# do this in the background as a daemon.
if (exists $ENV{INET6TO4_LOOP}) {
my $csl = $ENV{INET6TO4_LOOP};
if ($csl ne 'BG' && $csl ne '1') {
die "inet6to4: invalid INET6TO4_LOOP.\n";
}
if ($csl eq 'BG') {
# go into bg as "daemon":
setpgrp(0, 0);
my $pid = fork();
if (! defined $pid) {
die "inet6to4: $!\n";
} elsif ($pid) {
wait;
exit 0;
}
if (fork) {
exit 0;
}
setpgrp(0, 0);
close STDIN;
if (! $ENV{INET6TO4_LOGFILE}) {
close STDOUT;
close STDERR;
}
}
delete $ENV{INET6TO4_LOOP};
if (exists $ENV{INET6TO4_PIDFILE}) {
open_pidfile();
}
print STDERR "inet6to4: starting service at ", scalar(localtime), " master-pid=$$\n";
while (1) {
$looppid = fork;
if (! defined $looppid) {
sleep 10;
} elsif ($looppid) {
wait;
} else {
exec $0, @ARGV;
exit 1;
}
print STDERR "inet6to4: re-starting service at ", scalar(localtime), " master-pid=$$\n";
sleep 1;
}
exit 0;
}
if (exists $ENV{INET6TO4_PIDFILE}) {
open_pidfile();
}
use IO::Socket::INET6;
use strict;
use warnings;
# some settings:
#
my $verbose = 1; # set to 0 for no messages, 2 for more.
my $killpid = 1; # does kill(2) at end of connection.
my $waittime = 0.25; # time to wait between connections.
my $reverse = 0; # -r switch (or file named inet4to6)
if (exists $ENV{INET6TO4_VERBOSE}) {
$verbose = $ENV{INET6TO4_VERBOSE};
}
if (exists $ENV{INET6TO4_WAITTIME}) {
$waittime = $ENV{INET6TO4_WAITTIME};
}
# process command line args:
if (! @ARGV || $ARGV[0] =~ '^-+h') { # -help
open(ME, "<$0");
while (<ME>) {
last unless /^#/;
next if /usr.bin.perl/;
$_ =~ s/# ?//;
print;
}
exit;
}
if ($ARGV[0] eq '-r') { # -r
shift;
$reverse = 1;
} elsif ($0 =~ /inet4to6$/) {
$reverse = 1;
}
my $listen_port = shift; # ipv6-listen-port
my $connect_to = shift; # ipv4-host:port
die "no listen port or connect-to-host:port\n" if ! $listen_port || ! $connect_to;
# connect to host:
#
my $host = '';
my $port = '';
if ($connect_to =~ /^(.*):(\d+)$/) {
$host = $1;
$port = $2;
}
die "invalid connect-to-host:port\n" if ! $host || ! $port;
setpgrp(0, 0);
# create listening socket:
#
if (!$reverse) {
$listen_sock = IO::Socket::INET6->new(
Listen => 10,
LocalPort => $listen_port,
Domain => AF_INET6,
ReuseAddr => 1,
Proto => "tcp"
);
} else {
$listen_sock = IO::Socket::INET->new(
Listen => 10,
LocalPort => $listen_port,
ReuseAddr => 1,
Proto => "tcp"
);
}
if (! $listen_sock) {
die "inet6to4: $!\n";
}
# for use by the xfer helper processes' interrupt handlers:
#
my $current_fh1 = '';
my $current_fh2 = '';
# connection counter:
#
my $conn = 0;
# loop forever waiting for connections:
#
while (1) {
$conn++;
print STDERR "listening for connection: $conn\n" if $verbose;
my ($client, $ip) = $listen_sock->accept();
if ($client && !$reverse && $port == $listen_port) {
# This happens on Darwin 'tcp46'
if ($client->peerhost() =~ /^::ffff:/) {
print STDERR "closing client we think is actually us: ",
$client->peerhost(), "\n";
close $client;
$client = undef;
}
}
if (! $client) {
# to throttle runaways
fsleep(2 * $waittime);
next;
}
print STDERR "conn: $conn -- ", $client->peerhost(), " at ", scalar(localtime), "\n" if $verbose;
# spawn helper:
#
my $pid = fork();
if (! defined $pid) {
die "inet6to4: $!\n";
} elsif ($pid) {
wait;
# to throttle runaways
fsleep($waittime);
next;
} else {
# this is to avoid zombies:
close $listen_sock;
if (fork) {
exit 0;
}
setpgrp(0, 0);
handle_conn($client);
}
}
exit 0;
sub handle_conn {
my $client = shift;
my $start = time();
print STDERR "connecting to: $host:$port\n" if $verbose;
my $sock = '';
if (!$reverse) {
$sock = IO::Socket::INET->new(
PeerAddr => $host,
PeerPort => $port,
Proto => "tcp"
);
} else {
$sock = IO::Socket::INET6->new(
PeerAddr => $host,
PeerPort => $port,
Domain => AF_INET6,
Proto => "tcp"
);
}
if (! $sock) {
close $client;
die "inet6to4: $!\n";
}
$current_fh1 = $client;
$current_fh2 = $sock;
# interrupt handler:
#
$SIG{TERM} = sub {print STDERR "got sigterm\[$$]\n" if $verbose; close $current_fh1; close $current_fh2; exit 0};
# spawn another helper and transfer the data:
#
my $parent = $$;
if (my $child = fork()) {
xfer($sock, $client, 'S->C');
if ($killpid) {
fsleep(0.5);
kill 'TERM', $child;
}
} else {
xfer($client, $sock, 'C->S');
if ($killpid) {
fsleep(0.75);
kill 'TERM', $parent;
}
}
# done.
#
if ($verbose > 1) {
my $dt = time() - $start;
print STDERR "dt\[$$]: $dt\n";
}
exit 0;
}
# transfers data in one direction:
#
sub xfer {
my($in, $out, $lab) = @_;
my ($RIN, $WIN, $EIN, $ROUT);
$RIN = $WIN = $EIN = "";
$ROUT = "";
vec($RIN, fileno($in), 1) = 1;
vec($WIN, fileno($in), 1) = 1;
$EIN = $RIN | $WIN;
my $buf;
while (1) {
my $nf = 0;
while (! $nf) {
$nf = select($ROUT=$RIN, undef, undef, undef);
}
my $len = sysread($in, $buf, 8192);
if (! defined($len)) {
next if $! =~ /^Interrupted/;
print STDERR "inet6to4\[$lab/$conn/$$]: $!\n";
last;
} elsif ($len == 0) {
print STDERR "inet6to4\[$lab/$conn/$$]: "
. "Input is EOF.\n";
last;
}
if ($verbose > 4) {
# verbose debugging of data:
syswrite(STDERR , "\n$lab: ", 6);
syswrite(STDERR , $buf, $len);
}
my $offset = 0;
my $quit = 0;
while ($len) {
my $written = syswrite($out, $buf, $len, $offset);
if (! defined $written) {
print STDERR "inet6to4\[$lab/$conn/$$]: "
. "Output is EOF. $!\n";
$quit = 1;
last;
}
$len -= $written;
$offset += $written;
}
last if $quit;
}
close($in);
close($out);
}
# sleep a fraction of a second:
#
sub fsleep {
my ($time) = @_;
select(undef, undef, undef, $time) if $time;
}
#!/usr/bin/perl
#
# panner.pl: start up x11vnc in '-clip' mode viewing a small (WxH)
# rectangular region of the screen. Allow the viewer user
# to 'pan' around the display region by moving the mouse.
#
# Remote interaction with applications, e.g. clicking a
# button though the VNC viewer, will be very difficult.
# This may be useful in a 'demo' mode where the user sitting
# at the physical display is the only one moving the mouse.
# Depending on your usage the following x11vnc options may
# be useful: -nonap
#
# Usage: panner.pl WxH <x11vnc-args> (e.g. -display ...)
# or panner.pl WxH:0.05 <x11vnc-args> (e.g. 0.05 is polling time in secs.)
use strict;
my $WxH = shift;
my $poll_time;
# split off poll time:
#
($WxH, $poll_time) = split(/:/, $WxH);
my ($W, $H) = split(/x/, $WxH);
$poll_time = 0.1 unless $poll_time ne '';
# set to x11vnc command (e.g. full PATH)
#
my $x11vnc = "x11vnc";
# check if display was given:
#
my $query_args = "";
for (my $i=0; $i < @ARGV; $i++) {
if ($ARGV[$i] eq '-display') {
$query_args = "-display $ARGV[$i+1]";
}
}
# find the size of display and the current mouse position:
my %v;
vset("DIRECT:wdpy_x,wdpy_y,pointer_x,pointer_y,pointer_same");
# set a -clip argument based on the above:
#
my $clip = '';
clip_set();
$clip = "${W}x${H}+0+0" unless $v{pointer_same};
# launch x11vnc with -clip in the background:
#
my $cmd = "$x11vnc -clip $clip -bg " . join(" ", @ARGV);
print STDERR "running: $cmd\n";
system $cmd;
# user can hit Ctrl-C or kill this script to quit (and stop x11vnc)
#
sub quit {
system("$x11vnc $query_args -R stop");
exit 0;
}
$SIG{INT} = \&quit;
$SIG{TERM} = \&quit;
# loop forever waiting for mouse position to change, then shift -clip:
#
my $clip_old = $clip;
while (1) {
fsleep($poll_time);
vset("pointer_x,pointer_y,pointer_same");
next unless $v{pointer_same};
clip_set();
if ($clip ne $clip_old) {
system("$x11vnc $query_args -R clip:$clip");
$clip_old = $clip
}
}
exit 0;
# short sleep:
#
sub fsleep {
my ($time) = @_;
select(undef, undef, undef, $time) if $time;
}
# set the -clip string, making sure view doesn't go off edges of display:
#
sub clip_set {
my $x = int($v{pointer_x} - $W/2);
my $y = int($v{pointer_y} - $H/2);
$x = 0 if $x < 0;
$y = 0 if $y < 0;
$x = $v{wdpy_x} - $W if $x + $W > $v{wdpy_x};
$y = $v{wdpy_y} - $H if $y + $H > $v{wdpy_y};
$clip = "${W}x${H}+$x+$y";
}
# query x11vnc for values, put results in the %v hash:
#
sub vset {
my $str = shift;
my $out = `$x11vnc $query_args -Q $str 2>/dev/null`;
chomp $out;
foreach my $pair (split(/,/, $out)) {
$pair =~ s/^a..=//;
my ($k, $v) = split(/:/, $pair, 2);
if ($k ne '' && $v ne '') {
print STDERR "k=$k v=$v\n" if $ENV{DEBUG};
$v{$k} = $v;
}
}
}
#!/usr/bin/env perl
#
# Copyright (c) 2009 by Karl J. Runge <runge@karlrunge.com>
# Copyright (c) 2009-2010 by Karl J. Runge <runge@karlrunge.com>
#
# ultravnc_repeater.pl is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -41,17 +41,137 @@ usage: ultravnc_repeater.pl [-r] [client_port [server_port]]
Use -r to refuse new server/client connections with an existing
server/client ID. The default is to close the previous one.
To write to a log file set the env. var ULTRAVNC_REPEATER_LOGFILE.
To run in a loop restarting the server if it exits set the env. var.
ULTRAVNC_REPEATER_LOOP=1 or ULTRAVNC_REPEATER_LOOP=BG, the latter
forks into the background. Set ULTRAVNC_REPEATER_PIDFILE to a file
to store the master pid in.
Examples:
ultravnc_repeater.pl
ultravnc_repeater.pl -r
ultravnc_repeater.pl 5901
ultravnc_repeater.pl 5901 5501
env ULTRAVNC_REPEATER_LOOP=BG ULTRAVNC_REPEATER_LOGFILE=/tmp/u.log ultravnc_repeater.pl ...
';
use warnings;
use strict;
# Set up logging:
#
if (exists $ENV{ULTRAVNC_REPEATER_LOGFILE}) {
close STDOUT;
if (!open(STDOUT, ">>$ENV{ULTRAVNC_REPEATER_LOGFILE}")) {
die "ultravnc_repeater.pl: $ENV{ULTRAVNC_REPEATER_LOGFILE} $!\n";
}
close STDERR;
open(STDERR, ">&STDOUT");
}
select(STDERR); $| = 1;
select(STDOUT); $| = 1;
# interrupt handler:
#
my $looppid = '';
my $pidfile = '';
#
sub get_out {
print STDERR "$_[0]:\t$$ looppid=$looppid\n";
if ($looppid) {
kill 'TERM', $looppid;
fsleep(0.2);
}
unlink $pidfile if $pidfile;
cleanup();
exit 0;
}
# These are overridden in actual server thread:
#
$SIG{INT} = \&get_out;
$SIG{TERM} = \&get_out;
# pidfile:
#
sub open_pidfile {
if (exists $ENV{ULTRAVNC_REPEATER_PIDFILE}) {
my $pf = $ENV{ULTRAVNC_REPEATER_PIDFILE};
if (open(PID, ">$pf")) {
print PID "$$\n";
close PID;
$pidfile = $pf;
} else {
print STDERR "could not open pidfile: $pf - $! - continuing...\n";
}
delete $ENV{ULTRAVNC_REPEATER_PIDFILE};
}
}
####################################################################
# Set ULTRAVNC_REPEATER_LOOP=1 to have this script create an outer loop
# restarting itself if it ever exits. Set ULTRAVNC_REPEATER_LOOP=BG to
# do this in the background as a daemon.
if (exists $ENV{ULTRAVNC_REPEATER_LOOP}) {
my $csl = $ENV{ULTRAVNC_REPEATER_LOOP};
if ($csl ne 'BG' && $csl ne '1') {
die "ultravnc_repeater.pl: invalid ULTRAVNC_REPEATER_LOOP.\n";
}
if ($csl eq 'BG') {
# go into bg as "daemon":
setpgrp(0, 0);
my $pid = fork();
if (! defined $pid) {
die "ultravnc_repeater.pl: $!\n";
} elsif ($pid) {
wait;
exit 0;
}
if (fork) {
exit 0;
}
setpgrp(0, 0);
close STDIN;
if (! $ENV{ULTRAVNC_REPEATER_LOGFILE}) {
close STDOUT;
close STDERR;
}
}
delete $ENV{ULTRAVNC_REPEATER_LOOP};
if (exists $ENV{ULTRAVNC_REPEATER_PIDFILE}) {
open_pidfile();
}
print STDERR "ultravnc_repeater.pl: starting service at ", scalar(localtime), " master-pid=$$\n";
while (1) {
$looppid = fork;
if (! defined $looppid) {
sleep 10;
} elsif ($looppid) {
wait;
} else {
exec $0, @ARGV;
exit 1;
}
print STDERR "ultravnc_repeater.pl: re-starting service at ", scalar(localtime), " master-pid=$$\n";
sleep 1;
}
exit 0;
}
if (exists $ENV{ULTRAVNC_REPEATER_PIDFILE}) {
open_pidfile();
}
# End of background/daemon stuff.
####################################################################
use warnings;
use IO::Socket::INET;
use IO::Select;
......@@ -85,6 +205,7 @@ my ($RIN, $WIN, $EIN, $ROUT);
my $client_listen = IO::Socket::INET->new(
Listen => 10,
LocalPort => $client_port,
ReuseAddr => 1,
Proto => "tcp"
);
if (! $client_listen) {
......@@ -95,6 +216,7 @@ if (! $client_listen) {
my $server_listen = IO::Socket::INET->new(
Listen => 10,
LocalPort => $server_port,
ReuseAddr => 1,
Proto => "tcp"
);
if (! $server_listen) {
......@@ -103,7 +225,7 @@ if (! $server_listen) {
}
my $select = new IO::Select();
if (! select) {
if (! $select) {
cleanup();
die "$prog: select $!\n";
}
......@@ -120,9 +242,6 @@ my $CURR = '';
print "watching for connections on ports $server_port/server and $client_port/client\n";
select(STDERR); $| = 1;
select(STDOUT); $| = 1;
my $alarm_sock = '';
my $got_alarm = 0;
sub alarm_handler {
......
......@@ -1191,7 +1191,23 @@ char *process_remote_cmd(char *cmd, int stringonly) {
goto qry;
}
p += strlen("clip:");
if (clip_str) free(clip_str);
if (clip_str) {
int w, h, x, y;
free(clip_str);
/* try to handle easy case where WxH is unchanged: */
if (parse_geom(p, &w, &h, &x, &y, wdpy_x, wdpy_y)) {
if (cdpy_x == w && cdpy_y == h) {
if (x >= 0 && y >= 0) {
if (x + w <= wdpy_x && y + h <= wdpy_y) {
coff_x = x;
coff_y = y;
clip_str = strdup(p);
goto done;
}
}
}
}
}
clip_str = strdup(p);
/* OK, this requires a new fb... */
......@@ -5925,15 +5941,25 @@ char *process_remote_cmd(char *cmd, int stringonly) {
}
goto qry;
}
if (!strcmp(p, "pointer_pos")) {
if (!strcmp(p, "pointer_pos") || !strcmp(p, "pointer_x") || !strcmp(p, "pointer_y") || !strcmp(p, "pointer_same") || !strcmp(p, "pointer_root")) {
int px = -1, py = -1;
int wx, wy;
unsigned int m;
Window r, c;
Bool same_screen = True;
snprintf(buf, bufn, "aro=%s:%d,%d", p, px, py);
if (!strcmp(p, "pointer_pos")) { /* skip-cmd-list */
snprintf(buf, bufn, "aro=%s:%d,%d", p, px, py);
} else if (!strcmp(p, "pointer_x")) { /* skip-cmd-list */
snprintf(buf, bufn, "aro=%s:%d", p, px);
} else if (!strcmp(p, "pointer_y")) { /* skip-cmd-list */
snprintf(buf, bufn, "aro=%s:%d", p, py);
} else if (!strcmp(p, "pointer_same")) { /* skip-cmd-list */
snprintf(buf, bufn, "aro=%s:%d", p, same_screen);
} else if (!strcmp(p, "pointer_root")) { /* skip-cmd-list */
snprintf(buf, bufn, "aro=%s:0x%x", p, (unsigned int) rootwin);
}
if (!dpy) {
goto qry;
}
......@@ -5941,12 +5967,22 @@ char *process_remote_cmd(char *cmd, int stringonly) {
goto qry;
#else
X_LOCK;
XQueryPointer_wr(dpy, rootwin, &r, &c, &px, &py, &wx, &wy, &m);
same_screen = XQueryPointer_wr(dpy, rootwin, &r, &c, &px, &py, &wx, &wy, &m);
X_UNLOCK;
#endif
snprintf(buf, bufn, "aro=%s:%d,%d", p, px, py);
rfbLog("remote_cmd: pointer_pos: %s\n", buf);
if (!strcmp(p, "pointer_pos")) { /* skip-cmd-list */
snprintf(buf, bufn, "aro=%s:%d,%d", p, px, py);
} else if (!strcmp(p, "pointer_x")) { /* skip-cmd-list */
snprintf(buf, bufn, "aro=%s:%d", p, px);
} else if (!strcmp(p, "pointer_y")) { /* skip-cmd-list */
snprintf(buf, bufn, "aro=%s:%d", p, py);
} else if (!strcmp(p, "pointer_same")) { /* skip-cmd-list */
snprintf(buf, bufn, "aro=%s:%d", p, same_screen);
} else if (!strcmp(p, "pointer_root")) { /* skip-cmd-list */
snprintf(buf, bufn, "aro=%s:0x%x", p, (unsigned int) r);
}
rfbLog("remote_cmd: %s: %s\n", p, buf);
goto qry;
}
if (!strcmp(p, "bpp")) {
......
......@@ -3460,6 +3460,7 @@ void accept_openssl(int mode, int presock) {
char reply[] = "HTTP/1.0 200 OK\r\n"
"Content-Type: octet-stream\r\n"
"Connection: Keep-Alive\r\n"
"VNC-Server: x11vnc\r\n"
"Pragma: no-cache\r\n\r\n";
/*
* special case proxy coming thru https
......@@ -3503,6 +3504,7 @@ void accept_openssl(int mode, int presock) {
char reply[] = "HTTP/1.0 200 OK\r\n"
"Connection: close\r\n"
"Content-Type: octet-stream\r\n"
"VNC-Server: x11vnc\r\n"
"Pragma: no-cache\r\n\r\n";
rfbLog("Handling Check HTTPS request via https GET. [%d]\n", getpid());
......@@ -3782,14 +3784,14 @@ void accept_openssl(int mode, int presock) {
q = strstr(rcookie, "VENCRYPT=");
if (q && sscanf(q, "VENCRYPT=%d,", &vencrypt_sel) == 1) {
if (vencrypt_sel != 0) {
rfbLog("SSL: VENCRYPT mode=%d accepted.\n", vencrypt_sel);
rfbLog("SSL: VENCRYPT mode=%d accepted. helper[%d]\n", vencrypt_sel, pid);
goto accept_client;
}
}
q = strstr(rcookie, "ANONTLS=");
if (q && sscanf(q, "ANONTLS=%d,", &anontls_sel) == 1) {
if (anontls_sel != 0) {
rfbLog("SSL: ANONTLS mode=%d accepted.\n", anontls_sel);
rfbLog("SSL: ANONTLS mode=%d accepted. helper[%d]\n", anontls_sel, pid);
goto accept_client;
}
}
......@@ -3803,6 +3805,12 @@ void accept_openssl(int mode, int presock) {
if (strstr(rcookie, uniq) == rcookie) {
int i;
double https_download_wait_time = 15.0;
if (getenv("X11VNC_HTTPS_DOWNLOAD_WAIT_TIME")) {
https_download_wait_time = atof(getenv("X11VNC_HTTPS_DOWNLOAD_WAIT_TIME"));
}
rfbLog("SSL: BUT WAIT! HTTPS for helper process[%d] succeeded. Good.\n", pid);
if (mode != OPENSSL_HTTPS) {
last_https = dnow();
......@@ -3814,6 +3822,7 @@ void accept_openssl(int mode, int presock) {
}
if (rcookie && strstr(rcookie, "VncViewer.class")) {
rfbLog("\n");
rfbLog("helper[%d]:\n", pid);
rfbLog("***********************************************************\n");
rfbLog("SSL: WARNING CLIENT ASKED FOR NONEXISTENT 'VncViewer.class'\n");
rfbLog("SSL: USER NEEDS TO **RESTART** HIS WEB BROWSER.\n");
......@@ -3841,31 +3850,34 @@ void accept_openssl(int mode, int presock) {
}
screen->port = useport;
if (origport != useport) {
rfbLog("SSL: -httpsredir guess port: %d\n", screen->port);
rfbLog("SSL: -httpsredir guess port: %d helper[%d]\n", screen->port, pid);
}
start = dnow();
while (dnow() < start + 10.0) {
while (dnow() < start + https_download_wait_time) {
if (screen->httpSock >= 0) saw_httpsock = 1;
rfbPE(10000);
usleep(10000);
if (screen->httpSock >= 0) saw_httpsock = 1;
waitpid(pid, &status, WNOHANG);
if (kill(pid, 0) != 0) {
rfbPE(10000);
rfbPE(10000);
rfbLog("SSL: helper[%d] pid finished\n", pid);
break;
}
if (saw_httpsock && screen->httpSock < 0) {
if (0 && saw_httpsock && screen->httpSock < 0) {
/* this check can kill the helper too soon. */
rfbLog("SSL: httpSock for helper[%d] went away\n", pid);
rfbPE(10000);
rfbPE(10000);
break;
}
}
screen->port = origport;
rfbLog("SSL: guessing child helper[%d] https finished. dt=%.6f\n",
pid, dnow() - start);
rfbPE(10000);
rfbPE(10000);
rfbPE(10000);
screen->port = origport;
ssl_helper_pid(0, -2);
if (mode == OPENSSL_INETD) {
clean_up_exit(1);
......@@ -3888,31 +3900,34 @@ void accept_openssl(int mode, int presock) {
}
}
}
rfbLog("SSL: screen->port %d\n", screen->port);
rfbLog("SSL: screen->port %d for helper[%d]\n", screen->port, pid);
/* kludge for https fetch via inetd */
start = dnow();
while (dnow() < start + 10.0) {
while (dnow() < start + https_download_wait_time) {
if (screen->httpSock >= 0) saw_httpsock = 1;
rfbPE(10000);
usleep(10000);
if (screen->httpSock >= 0) saw_httpsock = 1;
waitpid(pid, &status, WNOHANG);
if (kill(pid, 0) != 0) {
rfbPE(10000);
rfbPE(10000);
rfbLog("SSL: helper[%d] pid finished\n", pid);
break;
}
if (saw_httpsock && screen->httpSock < 0) {
if (0 && saw_httpsock && screen->httpSock < 0) {
/* this check can kill the helper too soon. */
rfbLog("SSL: httpSock for helper[%d] went away\n", pid);
rfbPE(10000);
rfbPE(10000);
break;
}
}
rfbLog("SSL: OPENSSL_INETD guessing "
"child helper[%d] https finished. dt=%.6f\n",
pid, dnow() - start);
rfbPE(10000);
rfbPE(10000);
rfbPE(10000);
ssl_helper_pid(0, -2);
clean_up_exit(1);
}
......
......@@ -1603,7 +1603,7 @@ char create_display[] =
" fi\n"
" if [ -f \"$home/.dmrc\" ]; then\n"
" if [ \"X$have_startkde\" != \"X\" ]; then\n"
" if egrep -i 'Session=(default|kde)' \"$home/.dmrc\" > /dev/null; then\n"
" if egrep -i 'Session=kde' \"$home/.dmrc\" > /dev/null; then\n"
" echo \"$have_startkde\"\n"
" return\n"
" fi\n"
......@@ -1632,6 +1632,18 @@ char create_display[] =
" fi\n"
" \n"
" done\n"
" if egrep -i 'Session=default' \"$home/.dmrc\" > /dev/null; then\n"
" if [ \"X$have_gnome_session\" != \"X\" ]; then\n"
" echo \"$have_gnome_session\"\n"
" return\n"
" elif [ \"X$have_startkde\" != \"X\" ]; then\n"
" echo \"$have_startkde\"\n"
" return\n"
" elif [ \"X$have_startxfce\" != \"X\" ]; then\n"
" echo \"$have_startxfce\"\n"
" return\n"
" fi\n"
" fi\n"
" fi\n"
" if [ -f \"$home/.xsession\" ]; then\n"
" echo \"$home/.xsession\"\n"
......@@ -1851,19 +1863,30 @@ char create_display[] =
"\n"
" if [ \"X$use_xdmcp_query\" = \"X1\" ]; then\n"
" # we cannot use -nolisten tcp\n"
" echo \"$* -once -query localhost $FD_OPTS\" 1>&2\n"
" if [ \"X$FD_XDMCP_IF\" != \"X\" ]; then\n"
" lhost=$FD_XDMCP_IF\n"
" elif [ \"X$have_netstat\" = \"X\" ]; then\n"
" lhost=localhost\n"
" elif $have_netstat -an | grep -w 177 | grep -w udp > /dev/null; then\n"
" lhost=localhost\n"
" elif $have_netstat -an | grep -w 177 | grep -w udp6 > /dev/null; then\n"
" lhost=::1\n"
" else\n"
" lhost=localhost\n"
" fi\n"
" echo \"$* -once -query $lhost $FD_OPTS\" 1>&2\n"
" if [ \"X$have_root\" != \"X\" ]; then\n"
" if [ -r $authfile ]; then\n"
" $have_nohup $* -once -query localhost -auth $authfile $FD_OPTS 1>&2 &\n"
" $have_nohup $* -once -query $lhost -auth $authfile $FD_OPTS 1>&2 &\n"
" else\n"
" # why did we have this?\n"
" $have_nohup $* -once -query localhost $FD_OPTS 1>&2 &\n"
" $have_nohup $* -once -query $lhost $FD_OPTS 1>&2 &\n"
" fi\n"
" else\n"
" if [ \"X$ns\" = \"X0\" ]; then\n"
" $have_nohup sh -c \"$* -once -query localhost -auth $authfile $FD_OPTS\" 1>&2 &\n"
" $have_nohup sh -c \"$* -once -query $lhost -auth $authfile $FD_OPTS\" 1>&2 &\n"
" else\n"
" $have_nohup sh -c \"(sleep $ns; $* -once -query localhost -auth $authfile $FD_OPTS)\" 1>&2 &\n"
" $have_nohup sh -c \"(sleep $ns; $* -once -query $lhost -auth $authfile $FD_OPTS)\" 1>&2 &\n"
" #result=1\n"
" fi\n"
" fi\n"
......
......@@ -1956,7 +1956,7 @@ static char *build_create_cmd(char *cmd, int *saw_xdmcp, char *usslpeer, char *t
char st[] = "";
char fdgeom[128], fdsess[128], fdopts[128], fdextra[256], fdprog[128];
char fdxsrv[128], fdxdum[128], fdcups[128], fdesd[128];
char fdnas[128], fdsmb[128], fdtag[128];
char fdnas[128], fdsmb[128], fdtag[128], fdxdmcpif[128];
char cdout[128];
if (opts) {
......@@ -1980,6 +1980,7 @@ static char *build_create_cmd(char *cmd, int *saw_xdmcp, char *usslpeer, char *t
fdnas[0] = '\0';
fdsmb[0] = '\0';
fdtag[0] = '\0';
fdxdmcpif[0] = '\0';
cdout[0] = '\0';
if (unixpw && keep_unixpw_opts && keep_unixpw_opts[0] != '\0') {
......@@ -2122,6 +2123,9 @@ static char *build_create_cmd(char *cmd, int *saw_xdmcp, char *usslpeer, char *t
if (fdtag[0] == '\0' && getenv("FD_TAG")) {
snprintf(fdtag, 120, "%s", getenv("FD_TAG"));
}
if (fdxdmcpif[0] == '\0' && getenv("FD_XDMCP_IF")) {
snprintf(fdxdmcpif, 120, "%s", getenv("FD_XDMCP_IF"));
}
if (fdxdum[0] == '\0' && getenv("FD_XDUMMY_RUN_AS_ROOT")) {
snprintf(fdxdum, 120, "%s", getenv("FD_XDUMMY_RUN_AS_ROOT"));
}
......@@ -2139,6 +2143,7 @@ static char *build_create_cmd(char *cmd, int *saw_xdmcp, char *usslpeer, char *t
if (strchr(fdnas, '\'')) fdnas[0] = '\0';
if (strchr(fdsmb, '\'')) fdsmb[0] = '\0';
if (strchr(fdtag, '\'')) fdtag[0] = '\0';
if (strchr(fdxdmcpif, '\'')) fdxdmcpif[0] = '\0';
if (strchr(fdxdum, '\'')) fdxdum[0] = '\0';
if (strchr(fdsess, '\'')) fdsess[0] = '\0';
if (strchr(cdout, '\'')) cdout[0] = '\0';
......@@ -2153,6 +2158,7 @@ static char *build_create_cmd(char *cmd, int *saw_xdmcp, char *usslpeer, char *t
set_env("FD_NAS", fdnas);
set_env("FD_SMB", fdsmb);
set_env("FD_TAG", fdtag);
set_env("FD_XDMCP_IF", fdxdmcpif);
set_env("FD_XDUMMY_RUN_AS_ROOT", fdxdum);
set_env("FD_SESS", fdsess);
......@@ -2176,6 +2182,7 @@ static char *build_create_cmd(char *cmd, int *saw_xdmcp, char *usslpeer, char *t
+ strlen("FD_NAS='' ")
+ strlen("FD_SMB='' ")
+ strlen("FD_TAG='' ")
+ strlen("FD_XDMCP_IF='' ")
+ strlen("FD_XDUMMY_RUN_AS_ROOT='' ")
+ strlen("FD_SESS='' /bin/sh ")
+ strlen(uu) + 1
......@@ -2189,16 +2196,17 @@ static char *build_create_cmd(char *cmd, int *saw_xdmcp, char *usslpeer, char *t
+ strlen(fdnas) + 1
+ strlen(fdsmb) + 1
+ strlen(fdtag) + 1
+ strlen(fdxdmcpif) + 1
+ strlen(fdxdum) + 1
+ strlen(fdsess) + 1
+ strlen(cdout) + 1
+ strlen(opts) + 1);
sprintf(create_cmd, "env USER='%s' FD_GEOM='%s' FD_SESS='%s' "
"FD_OPTS='%s' FD_EXTRA='%s' FD_PROG='%s' FD_XSRV='%s' FD_CUPS='%s' "
"FD_ESD='%s' FD_NAS='%s' FD_SMB='%s' FD_TAG='%s' "
"FD_ESD='%s' FD_NAS='%s' FD_SMB='%s' FD_TAG='%s' FD_XDMCP_IF='%s' "
"FD_XDUMMY_RUN_AS_ROOT='%s' %s /bin/sh %s %s",
uu, fdgeom, fdsess, fdopts, fdextra, fdprog, fdxsrv,
fdcups, fdesd, fdnas, fdsmb, fdtag, fdxdum, cdout, tmp, opts);
fdcups, fdesd, fdnas, fdsmb, fdtag, fdxdmcpif, fdxdum, cdout, tmp, opts);
} else {
create_cmd = (char *) malloc(strlen(tmp)
+ strlen("/bin/sh ") + 1 + strlen(opts) + 1);
......
.\" This file was automatically generated from x11vnc -help output.
.TH X11VNC "1" "February 2010" "x11vnc " "User Commands"
.TH X11VNC "1" "March 2010" "x11vnc " "User Commands"
.SH NAME
x11vnc - allow VNC connections to real X11 displays
version: 0.9.10, lastmod: 2010-02-21
version: 0.9.10, lastmod: 2010-03-20
.SH SYNOPSIS
.B x11vnc
[OPTION]...
......@@ -558,6 +558,11 @@ to the program location and in standard locations
\fB-http_ssl\fR
.IP
As \fB-http,\fR but force lookup for ssl classes subdir.
.IP
Note that for HTTPS, single-port Java applet delivery
you can set X11VNC_HTTPS_DOWNLOAD_WAIT_TIME to the
max number of seconds to wait for the applet download
to finish. The default is 15.
.PP
\fB-avahi\fR
.IP
......@@ -1061,9 +1066,31 @@ Use "deny" to explicitly deny some users if you use
the user is allowed, but the option values associated
with it do apply as normal.
.IP
There are also some utilities for testing password
There are also some utilities for checking passwords
if [list] starts with the "%" character. See the
quick_pw() function in the source for details.
quick_pw() function for more details. Description:
"%-" or "%stdin" means read one line from stdin.
"%env" means it is in $UNIXPW env var. A leading
"%/" or "%." means read the first line from the
filename that follows after the % character. % by
itself means prompt for the username and password.
Otherwise: %user:pass E.g. \fB-unixpw\fR %fred:swordfish
For the other cases user:pass is read from the indicated
source. If the password is correct 'Y user' is printed
and the program exit code is 0. If the password is
incorrect it prints 'N user' and the exit code is 1.
If there is some other error the exit code is 2.
This feature enables x11vnc to be a general unix user
password checking tool; it could be used from scripts
or other programs. These % password checks also apply
to the \fB-unixpw_nis\fR and \fB-unixpw_cmd\fR options.
.IP
For the % password check, if the env. var. UNIXPW_CMD
is set to a command then it is run as the user (assuming
the password is correct.) The output of the command is
not printed, the program or script must manage that by
some other means. The exit code of x11vnc will depend
on the exit code of the command that is run.
.IP
Use \fB-nounixpw\fR to disable unixpw mode if it was enabled
earlier in the cmd line (e.g. \fB-svc\fR mode)
......@@ -1184,8 +1211,11 @@ and if it has the permissions to do so.
.PP
\fB-find\fR
.IP
Find the user's display using FINDDISPLAY. This is an
alias for "\fB-display\fR \fIWAIT:cmd=FINDDISPLAY\fR".
Find the user's display using FINDDISPLAY. This
is an alias for "\fB-display\fR \fIWAIT:cmd=FINDDISPLAY\fR".
.IP
Note: if a \fB-display\fR occurs later on the command line
it will override the \fB-find\fR setting.
.IP
For this and the next few options see \fB-display\fR WAIT:...
below for all of the details.
......@@ -1232,6 +1262,9 @@ if that doesn't succeed create an X session via the
FINDCREATEDISPLAY method. This is an alias for
"\fB-display\fR \fIWAIT:cmd=FINDCREATEDISPLAY-Xvfb\fR".
.IP
Note: if a \fB-display\fR occurs later on the command line
it will override the \fB-create\fR setting.
.IP
SSH NOTE: for both \fB-find\fR and \fB-create\fR you can (should!)
add the "\fB-localhost\fR" option to force SSH tunnel access.
.PP
......@@ -1263,6 +1296,10 @@ Example: \fB-svc\fR ... \fB-create_xsrv\fR Xdummy,X
Terminal services mode based on SSL access. Alias for
\fB-display\fR WAIT:cmd=FINDCREATEDISPLAY-Xvfb \fB-unixpw\fR \fB-users\fR
unixpw= \fB-ssl\fR SAVE Also "\fB-service\fR".
.IP
Note: if a \fB-display,\fR \fB-unixpw,\fR \fB-users,\fR or \fB-ssl\fR occurs
later on the command line it will override the \fB-svc\fR
setting.
.PP
\fB-svc_xdummy\fR
.IP
......@@ -1282,6 +1319,10 @@ Display manager Terminal services mode based on SSL.
Alias for \fB-display\fR WAIT:cmd=FINDCREATEDISPLAY-Xvfb.xdmcp
\fB-unixpw\fR \fB-users\fR unixpw= \fB-ssl\fR SAVE Also "\fB-xdm_service\fR".
.IP
Note: if a \fB-display,\fR \fB-unixpw,\fR \fB-users,\fR or \fB-ssl\fR occurs
later on the command line it will override the \fB-xdmsvc\fR
setting.
.IP
To create a session a user will have to first log in
to the \fB-unixpw\fR dialog and then log in again to the
XDM/GDM/KDM prompt. Subsequent re-connections will
......@@ -1654,6 +1695,11 @@ to be a unique name for the session, it is set as an
X property, that makes FINDDISPLAY only find sessions
with that tag value.
.IP
Set FD_XDMCP_IF to the network interface that the
display manager is running on; default is 'localhost'
but you may need to set it to '::1' on some IPv6 only
systems or misconfigured display managers.
.IP
If you want the FINDCREATEDISPLAY session to contact an
XDMCP login manager (xdm/gdm/kdm) on the same machine,
then use "Xvfb.xdmcp" instead of "Xvfb", etc.
......@@ -2370,7 +2416,7 @@ exits.
.IP
Use the
.IR stunnel (8)
(www.stunnel.org) to provide an
(stunnel.mirt.net) to provide an
encrypted SSL tunnel between viewers and x11vnc.
.IP
This external tunnel method was implemented prior to the
......@@ -5838,6 +5884,14 @@ grab_state get state of pointer and keyboard grab.
.IP
pointer_pos print XQueryPointer x,y cursor position.
.IP
pointer_x print XQueryPointer x cursor position.
.IP
pointer_y print XQueryPointer y cursor position.
.IP
pointer_same print XQueryPointer ptr on same screen.
.IP
pointer_root print XQueryPointer curr ptr rootwin.
.IP
mouse_x print x11vnc's idea of cursor position.
.IP
mouse_y print x11vnc's idea of cursor position.
......@@ -6234,18 +6288,18 @@ loop loopbg desktopname guess_desktop guess_dbus
http_url auth xauth users rootshift clipshift scale_str
scaled_x scaled_y scale_numer scale_denom scale_fac_x
scale_fac_y scaling_blend scaling_nomult4 scaling_pad
scaling_interpolate inetd privremote unsafe safer
nocmds passwdfile unixpw unixpw_nis unixpw_list ssl
ssl_pem sslverify stunnel stunnel_pem https httpsredir
usepw using_shm logfile o flag rmflag rc norc h help
V version lastmod bg sigpipe threads readrate netrate
netlatency pipeinput clients client_count pid ext_xtest
ext_xtrap ext_xrecord ext_xkb ext_xshm ext_xinerama
ext_overlay ext_xfixes ext_xdamage ext_xrandr rootwin
num_buttons button_mask mouse_x mouse_y grab_state
pointer_pos bpp depth indexed_color dpy_x dpy_y wdpy_x
wdpy_y off_x off_y cdpy_x cdpy_y coff_x coff_y rfbauth
passwd viewpasswd
scaling_interpolate inetd privremote unsafe safer nocmds
passwdfile unixpw unixpw_nis unixpw_list ssl ssl_pem
sslverify stunnel stunnel_pem https httpsredir usepw
using_shm logfile o flag rmflag rc norc h help V version
lastmod bg sigpipe threads readrate netrate netlatency
pipeinput clients client_count pid ext_xtest ext_xtrap
ext_xrecord ext_xkb ext_xshm ext_xinerama ext_overlay
ext_xfixes ext_xdamage ext_xrandr rootwin num_buttons
button_mask mouse_x mouse_y grab_state pointer_pos
pointer_x pointer_y pointer_same pointer_root bpp depth
indexed_color dpy_x dpy_y wdpy_x wdpy_y off_x off_y
cdpy_x cdpy_y coff_x coff_y rfbauth passwd viewpasswd
.PP
\fB-QD\fR \fIvariable\fR
.IP
......
......@@ -1321,10 +1321,10 @@ static void quick_pw(char *str) {
if (db) fprintf(stderr, "quick_pw: %s\n", str);
if (! str || str[0] == '\0') {
exit(1);
exit(2);
}
if (str[0] != '%') {
exit(1);
exit(2);
}
/*
* "%-" or "%stdin" means read one line from stdin.
......@@ -1339,19 +1339,19 @@ static void quick_pw(char *str) {
*/
if (!strcmp(str, "%-") || !strcmp(str, "%stdin")) {
if(fgets(tmp, 1024, stdin) == NULL) {
exit(1);
exit(2);
}
q = strdup(tmp);
} else if (!strcmp(str, "%env")) {
if (getenv("UNIXPW") == NULL) {
exit(1);
exit(2);
}
q = strdup(getenv("UNIXPW"));
} else if (!strcmp(str, "%%") || !strcmp(str, "%")) {
char *t, inp[1024];
fprintf(stdout, "username: ");
if(fgets(tmp, 128, stdin) == NULL) {
exit(1);
exit(2);
}
strcpy(inp, tmp);
t = strchr(inp, '\n');
......@@ -1367,7 +1367,7 @@ static void quick_pw(char *str) {
if(fgets(tmp, 128, stdin) == NULL) {
fprintf(stdout, "\n");
system("stty echo");
exit(1);
exit(2);
}
system("stty echo");
fprintf(stdout, "\n");
......@@ -1376,10 +1376,10 @@ static void quick_pw(char *str) {
} else if (str[1] == '/' || str[1] == '.') {
FILE *in = fopen(str+1, "r");
if (in == NULL) {
exit(1);
exit(2);
}
if(fgets(tmp, 1024, in) == NULL) {
exit(1);
exit(2);
}
q = strdup(tmp);
} else {
......@@ -1392,7 +1392,7 @@ static void quick_pw(char *str) {
}
if ((q = strchr(p, ':')) == NULL) {
exit(1);
exit(2);
}
*q = '\0';
if (db) fprintf(stderr, "'%s' '%s'\n", p, q+1);
......@@ -1413,7 +1413,8 @@ static void quick_pw(char *str) {
exit(1);
}
} else {
if (su_verify(p, q+1, NULL, NULL, NULL, 1)) {
char *ucmd = getenv("UNIXPW_CMD");
if (su_verify(p, q+1, ucmd, NULL, NULL, 1)) {
fprintf(stdout, "Y %s\n", p);
exit(0);
} else {
......@@ -1422,7 +1423,7 @@ static void quick_pw(char *str) {
}
}
/* NOTREACHED */
exit(1);
exit(2);
}
static void print_settings(int try_http, int bg, char *gui_str) {
......@@ -2629,7 +2630,7 @@ int main(int argc, char* argv[]) {
if (s[0] == '%') {
unixpw_list = NULL;
quick_pw(s);
exit(1);
exit(2);
}
}
if (strstr(arg, "_unsafe")) {
......
......@@ -47,7 +47,7 @@ int xtrap_base_event_type = 0;
int xdamage_base_event_type = 0;
/* date +'lastmod: %Y-%m-%d' */
char lastmod[] = "0.9.10 lastmod: 2010-02-21";
char lastmod[] = "0.9.10 lastmod: 2010-03-20";
/* X display info */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment