#!/bin/sh # # ssl_vncviewer: wrapper for vncviewer to use an stunnel SSL tunnel. # # Copyright (c) 2006 by Karl J. Runge # # You must have stunnel(8) installed on the system and in your # PATH (n.b. stunnel is usually in an sbin subdir). # # You should have "x11vnc -ssl ..." or "x11vnc -stunnel ..." # running as the VNC server. # # usage: ssl_vncviewer [cert-args] host:display # # e.g.: ssl_vncviewer snoopy:0 # ssl_vncviewer snoopy:0 -encodings "copyrect tight zrle hextile" # # [cert-args] can be: # -verify /path/to/cacert.pem # -mycert /path/to/mycert.pem # -proxy host:port # # -verify specifies a CA cert PEM file (or a self-signed one) for # authenticating the VNC server. # # -mycert specifies this client's cert+key PEM file for the VNC server to # authenticate this client. # # -proxy try host:port as a Web proxy to use the CONNECT method # to reach the VNC server (e.g. your firewall requires a proxy). # For the "double proxy" case use -proxy host1:port1,host2:port2 # # # set VNCVIEWERCMD to whatever vncviewer command you want to use: # VNCVIEWERCMD=${VNCVIEWERCMD:-vncviewer} # # Same for STUNNEL, e.g. /path/to/stunnel or stunnel4, etc. # PATH=$PATH:/usr/sbin:/usr/local/sbin:/dist/sbin; export PATH if [ "X$STUNNEL" = "X" ]; then type stunnel4 > /dev/null 2>&1 if [ $? = 0 ]; then STUNNEL=stunnel4 else STUNNEL=stunnel fi fi help() { head -39 $0 | tail +2 } # grab our cmdline options: while [ "X$1" != "X" ] do case $1 in "-verify") shift; verify="$1" ;; "-mycert") shift; mycert="$1" ;; "-proxy") shift; proxy="$1" ;; "-h"*) help; exit 0 ;; *) break ;; esac shift done orig="$1" shift # play around with host:display port: if ! echo "$orig" | grep ':' > /dev/null; then orig="$orig:0" fi host=`echo "$orig" | awk -F: '{print $1}'` disp=`echo "$orig" | awk -F: '{print $2}'` if [ $disp -lt 200 ]; then port=`expr $disp + 5900` else port=$disp fi # try to find an open listening port via netstat(1): use="" inuse="" if uname | grep Linux > /dev/null; then inuse=`netstat -ant | grep LISTEN | awk '{print $4}' | sed 's/^.*://'` elif uname | grep SunOS > /dev/null; then inuse=`netstat -an -f inet -P tcp | grep LISTEN | awk '{print $1}' | sed 's/^.*\.//'` fi if [ "x$inuse" != "x" ]; then try=5920 while [ $try -lt 6000 ] do if ! echo "$inuse" | grep -w $try > /dev/null; then use=$try break fi try=`expr $try + 1` done fi if [ "X$use" = "X" ]; then # otherwise choose a "random" one: use=`date +%S` use=`expr $use + 5920` fi # create the stunnel config file: if [ "X$verify" != "X" ]; then if [ -d $verify ]; then verify="CApath = $verify" else verify="CAfile = $verify" fi verify="$verify verify = 2" fi if [ "X$mycert" != "X" ]; then cert="cert = $mycert" fi pcode() { tf=$1 SSL_VNC_PROXY=$proxy; export SSL_VNC_PROXY SSL_VNC_DEST="$host:$port"; export SSL_VNC_DEST cod='#!/usr/bin/perl # A hack to glue stunnel to a Web proxy for client connections. use IO::Socket::INET; my ($first, $second) = split(/,/, $ENV{SSL_VNC_PROXY}); my ($proxy_host, $proxy_port) = split(/:/, $first); my $connect = $ENV{SSL_VNC_DEST}; print STDERR "\nperl script for web proxing:\n"; print STDERR "proxy_host: $proxy_host\n"; print STDERR "proxy_port: $proxy_port\n"; print STDERR "proxy_connect: $connect\n"; my $sock = IO::Socket::INET->new( PeerAddr => $proxy_host, PeerPort => $proxy_port, Proto => "tcp"); if (! $sock) { unlink($0); die "perl proxy: $!\n"; } my $con = ""; if ($second ne "") { $con = "CONNECT $second HTTP/1.1\r\n"; $con .= "Host: $second\r\n\r\n"; } else { $con = "CONNECT $connect HTTP/1.1\r\n"; $con .= "Host: $connect\r\n\r\n"; } print STDERR "proxy_request1:\n$con"; print $sock $con; unlink($0); my $rep = ""; while ($rep !~ /\r\n\r\n/) { my $c = getc($sock); print STDERR $c; $rep .= $c; } if ($rep !~ m,HTTP/.* 200,) { die "proxy error: $rep\n"; } if ($second ne "") { $con = "CONNECT $connect HTTP/1.1\r\n"; $con .= "Host: $connect\r\n\r\n"; print STDERR "proxy_request2:\n$con"; print $sock $con; $rep = ""; while ($rep !~ /\r\n\r\n/) { my $c = getc($sock); print STDERR $c; $rep .= $c; } if ($rep !~ m,HTTP/.* 200,) { die "proxy error: $rep\n"; } } if (fork) { print STDERR "parent\[$$] STDIN -> socket\n\n"; xfer(STDIN, $sock); } else { print STDERR "child \[$$] socket -> STDOUT\n\n"; xfer($sock, STDOUT); } exit; sub xfer { my($in, $out) = @_; $RIN = $WIN = $EIN = ""; $ROUT = ""; vec($RIN, fileno($in), 1) = 1; vec($WIN, fileno($in), 1) = 1; $EIN = $RIN | $WIN; 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 "perl proxy\[$$]: $!\n"; last; } elsif ($len == 0) { print STDERR "perl proxy\[$$]: Input is EOF.\n"; last; } my $offset = 0; my $quit = 0; while ($len) { my $written = syswrite($out, $buf, $len, $offset); if (! defined $written) { print STDERR "perl proxy\[$$]: Output is EOF. $!\n"; $quit = 1; last; } $len -= $written; $offset += $written; } last if $quit; } close($in); close($out); } ' rm -f $tf if [ -f $tf ]; then echo "$tf still exists!" exit 1 fi echo "$cod" > $tf chmod 700 $tf } ptmp="" if [ "X$proxy" != "X" ]; then ptmp="/tmp/ssl_vncviewer.$$.pl" pcode $ptmp connect="exec = $ptmp" else connect="connect = $host:$port" fi ##debug = 7 tmp=/tmp/ssl_vncviewer.$$ cat > $tmp < /dev/tty & pid=$! echo "" # pause here to let the user supply a possible passphrase for the # mycert key: if [ "X$mycert" != "X" ]; then sleep 4 fi sleep 2 rm -f $tmp if [ $use -ge 5900 ]; then n=`expr $use - 5900` fi if echo "$0" | grep vncip > /dev/null; then # hack for runge's special wrapper script vncip. vncip "$@" localhost:$n else $VNCVIEWERCMD "$@" localhost:$n fi kill $pid sleep 1