diff options
Diffstat (limited to 'x11vnc/misc/desktop.cgi')
-rwxr-xr-x | x11vnc/misc/desktop.cgi | 1550 |
1 files changed, 0 insertions, 1550 deletions
diff --git a/x11vnc/misc/desktop.cgi b/x11vnc/misc/desktop.cgi deleted file mode 100755 index d99a39c..0000000 --- a/x11vnc/misc/desktop.cgi +++ /dev/null @@ -1,1550 +0,0 @@ -#!/usr/bin/perl -# -########################################################################## -# desktop.cgi: -# -# This is an example CGI script to provide multi-user web access to -# x11vnc desktops. The user desktop sessions run in 'Xvfb' displays -# that are created automatically. -# -# This script should/must be served by an HTTPS (i.e. SSL) webserver, -# otherwise the unix and vnc passwords would be sent over the network -# unencrypted (see below to disable if you really want to.) -# -# The Java VNC Viewer applet connections are encrypted by SSL as well. -# -# You can use this script to provide unix users desktops available on -# demand via any Java enabled web browser. One could also use this for -# a special-purpose 'single application' service running in a minimal -# window manager. -# -# One example of a special-purpose application would be a scientific -# data visualization tool running on a server where the data is housed. -# To do this set $x11vnc_extra_opts = '-env FD_PROG=/path/to/app/launcher' -# where the program launches your special purpose application. A very -# simple example: '-env FD_PROG=/usr/bin/xclock' -# -# -# Depending on where you place this script, the user accesses the service -# with the URL: -# -# https://your.webserver.net/cgi-bin/desktop.cgi -# -# Then they login with their unix username and password to get their -# own desktop session. -# -# If the user has an existing desktop it is connected to directly, -# otherwise a new session is created inside an Xvfb display and then -# connected to by VNC. -# -# It is possible to do port redirection to other machines running SSL -# enabled VNC servers (see below.) This script does not start the VNC -# servers on the other machines, although with some extra rigging you -# should be able to do that as well. -# -# You can customize the login procedure to whatever you want by modifying -# this script, or by using ideas in this script write your own PHP, -# (for example), script. -# -########################################################################## -# Overriding default settings: -# -# If you want to override any settings in this script and do not -# want to edit this script create the assignments in a file named -# 'desktop.cgi.conf' in the same directory as desktop.cgi. It will be -# sourced after the defaults are set. The format of desktop.cgi.conf -# is simply perl statements that make the assignments. -# -# For example, if you put something like this in desktop.cgi.conf: -# -# $x11vnc = '/usr/local/bin/x11vnc'; -# -# that will set the path to the x11vnc binary to that location. Look at -# the settings below for the other variables that you can modify, for -# example one could set $allowed_users_file. -# -########################################################################## -# x11vnc: -# -# You need to install x11vnc or otherwise have it available. It is -# REQUIRED that you use x11vnc 0.9.10 or later. It won't work with -# earlier versions. See below the $x11vnc parameter that you can set -# to the full path to x11vnc. -# -########################################################################## -# Xvfb: -# -# Note that the x11vnc -create virtual desktop service used below requires -# that you install the 'Xvfb' program. On debian this is currently done -# via 'apt-get install xvfb'. -# -# If you are having trouble getting 'x11vnc -create' to work with this -# script (it can be tricky), try it manually and/or see the x11vnc FAQ -# links below. -# -########################################################################## -# Apache httpd: -# -# You should put this script in, say, a cgi-bin directory. Enable cgi -# scripts in your apache (or other httpd) config. For example, we have -# these lines (not commented): -# -# In httpd.conf: -# -# ScriptAlias /cgi-bin/ "/dist/apache/2.0/cgi-bin/" -# -# <Directory "/dist/apache/2.0/cgi-bin"> -# AllowOverride None -# Options None -# Order allow,deny -# Allow from all -# </Directory> -# -# and in ssl.conf: -# -# <Directory "/dist/apache/2.0/cgi-bin"> -# SSLOptions +StdEnvVars -# </Directory> -# -# Do not be confused by the non-standard /dist/apache/2.0 apache -# installation location that we happen to use. Yours will be different. -# -# You can test that you have CGI scripts working properly with the -# 'test-cgi' and 'printenv' scripts apache provides. -# -# Copy this file (desktop.cgi) to /dist/apache/2.0/cgi-bin and then run -# 'chmod 755 ...' on it to make it executable. -# -########################################################################## -# Applet Jar files served by apache: -# -# You will *also* need to copy the x11vnc classes/ssl/UltraViewerSSL.jar -# file to the httpd DocumentRoot to be accessible by: /UltraViewerSSL.jar -# in a URL (or change $applet_jar below or the html in $applet_html if -# you want to use a different location.) -# -# This location is relative to the apache DocumentRoot 'htdocs' directory. -# For our (non-standard location installation) that meant we copied the -# file to: -# -# /dist/apache/2.0/htdocs/UltraViewerSSL.jar -# -# (your DocumentRoot directory will be different.) -# -# The VncViewer.jar (tightvnc) will also work, but you need to change -# the $applet_jar below. You can get these jar files from the x11vnc -# tarball from: -# -# http://www.karlrunge.com/x11vnc/#downloading -# -# This script requires x11vnc 0.9.10 or later. -# -# Note that the usage mode for this script is a different from regular -# 'x11vnc -http ...' usage where x11vnc acts as a mini web server and -# serves its own applet jars. We don't use that mode for this script. -# Apache (httpd) serves the jars. -# -# -########################################################################## -# Notes and Information: -# -# Each x11vnc server created for a user login will listen on its own port -# (see below for port selection schemes.) Your firewall must let in *ALL* -# of these ports (e.g. a port range, see below for the syntax.) -# -# It is also possible, although not as reliable, to do all of this through -# a single port, see the fixed port scheme $find_free_port = 'fixed:5910' -# below. This single port mode must be different from apache's port -# (usually 443 for https) and must also be allowed in by your firewall. -# -# Note: The fixed port scheme is DISABLED by default. -# -# It is also possible to have this script act as a vnc redirector to SSL -# enabled VNC servers running on *other* machines inside your firewall -# (presumably the users' desktops) See the $enable_port_redirection -# setting below. The user provides 'username@host:port' instead of just -# 'username' when she logs in. This script doesn't start VNC servers -# on those other machines, the servers must be running there already. -# (If you want this script to start them you will need to add it -# yourself.) It is possible to provide a host:port allow list to limit -# which internal machines and ports can be redirected to. This is the -# $port_redirection_allowed_hosts parameter. -# -# Note: The vnc redirector scheme is DISABLED by default. -# -# 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 naive users. You may want to use the same cert for both. -# -# This script provides one example on how to provide the service. You can -# customize it to meet your needs, e.g. switch to php, newer cgi modules, -# different authentication, SQL database for user authentication, etc, -# 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 and troubleshooting: -# -# 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 -# -# -# Please also read the comments below for changing specific settings. -# You can modify them in this script or by override file 'desktop.cgi.conf' - - -#------------------------------------------------------------------------- -# 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; - -# Test for INET6 support: -# -my $have_inet6 = 0; -eval "use IO::Socket::INET6;"; -$have_inet6 = 1 if $@ eq ""; - -########################################################################## -# Path to the x11vnc program: -# -my $x11vnc = '/usr/bin/x11vnc'; - - -########################################################################## -# You can set some extra x11vnc cmdline options here: -# -my $x11vnc_extra_opts = ''; - - -########################################################################## -# Override the default x11vnc viewer connection timeout of 75 seconds: -# -my $x11vnc_timeout = ''; - - -########################################################################## -# 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 port based on -# the userid number (7000 + uid). -# -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 the single port. -# -#$find_free_port = 'fixed:5910'; - -# This is the starting port for 7000 + uid and also $find_free_port = 1 -# autoselection: -# -my $starting_port = 7000; - -# Listen on AF_INET6 if IO::Socket::INET6 is available. -# -my $listen_on_ipv6 = 0; - - -########################################################################## -# Port redirection mode: -# -# This is to enable 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 VNC server. -# (You might be able to figure out a way to do this yourself.) -# -# 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 = ''; - - -########################################################################## -# Allowed users: -# -# To limit which users can use this service, set the following to a file -# that contains the allowed user names one per line. Lines starting with -# the '#' character are skipped. -# -my $allowed_users_file = ''; - - -########################################################################## -# Denied users: -# -# As with $allowed_users_file, but to deny certain users. Applied after -# any $allowed_users_file check and overrides the result. -# -my $denied_users_file = ''; - - -########################################################################## -# trustUrlVncCert applet parameter: -# -# 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 $applet_html and -# print_applet_html() below for more info. -# -my $trustUrlVncCert = 1; - - -########################################################################## -# One-time VNC password fifo: -# -# For extra security against local untrusted users a fifo is used -# to copy the one-time VNC password to the user's VNC password file -# ~user/x11vnc.pw. If that fifo transfer technique causes problems, -# you can set this value to 1 to disable the security feature: -# -my $disable_vnc_passwd_fifo_safety = 0; - - -########################################################################## -# 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 = ''; - - -########################################################################## -# String of HTML for the login form: -# -# Feel free to customize to your taste, _USERNAME_ and _GEOMETRY_ are -# expanded to that of the request. -# -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="_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="_GEOMETRY_"> - </td></tr> - <!-- session --> - <tr><td colspan="2" align="right"> - <input type="submit" name="submit" value="Login"> - </td></tr> - </table> -</form> -END - - -########################################################################## -# String of HTML returned to web browser to launch applet: -# -# Feel free to customize to your taste, _UID_, _VNC_PORT_, _WIDTH_, -# _HEIGHT_, _PASS_, _TRUST_UVC_, _APPLET_JAR_, and _APPLET_CLASS_ are -# expanded to the appropriate values before sending out to the browser. -# -my $applet_html = <<"END"; -<html> -<TITLE> -x11vnc desktop (_UID_/_VNC_PORT_) -</TITLE> -<APPLET CODE=_APPLET_CLASS_ ARCHIVE=_APPLET_JAR_ WIDTH=_WIDTH_ HEIGHT=_HEIGHT_> -<param name=PORT value=_VNC_PORT_> -<param name=VNCSERVERPORT value=_VNC_PORT_> -<param name=PASSWORD value=_PASS_> -<param name=trustUrlVncCert value=_TRUST_UVC_> -<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 - - -########################################################################## -# These java applet strings are expanded into the above $applet_html. -# Note that $applet_jar is relative to your apache DocumentRoot (htdocs) -# not the filesystem root. -# -my $applet_jar = '/UltraViewerSSL.jar'; -my $applet_class = 'VncViewer.class'; - -# These make the applet panel smaller because we use 'Open New Window' -# anyway (set to 'W' or 'H' to use actual session geometry values): -# -my $applet_width = '400'; -my $applet_height = '300'; - -# To customize ALL of the HTML printed out you may need to redefine -# the bye() subtroutine in your desktop.cgi.conf file. - - -########################################################################## -# Override any of the above settings by setting them in a file named -# 'desktop.cgi.conf'. It is sourced here. -# -# You can override any variable set above by supplying perl code -# in $0.conf that sets it to the desired value. -# -# Some examples you could put in $0.conf: -# -# $x11vnc = '/usr/local/bin/x11vnc'; -# $x11vnc_extra_opts = '-env FD_PROG=/usr/bin/xclock'; -# $x11vnc_extra_opts = '-ssl /usr/local/etc/dtcgi.pem'; -# $find_free_port = 'fixed:5999'; -# $enable_port_redirection = 1; -# $allowed_users_file = '/usr/local/etc/dtcgi.allowed'; -# -if (-f "$0.conf") { - eval `cat "$0.conf"`; -} - - -########################################################################## -# END OF MAIN USER SETTINGS. -# Only power users should change anything below. -########################################################################## - -# Print http header reply: -# -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 here at your own risk. -# -if ($ENV{HTTPS} !~ /^on$/i) { - bye("HTTPS must be used (to encrypt passwords)"); -} - - -# Read URL 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 for a -# single user: -# -# 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} = ''; -} - - -# Expand _USERNAME_ and _GEOMETRY_ in the login string HTML: -# -$login_str =~ s/_USERNAME_/$request{username}/g; -$login_str =~ s/_GEOMETRY_/$request{geometry}/g; - - -# Check x11vnc version for installers of this script who do not know -# how to read and follow instructions: -# -my $version = (split(' ', `$x11vnc -version`))[1]; -$version =~ s/\D*$//; - -my ($major, $minor, $micro) = split(/\./, $version); -if ($major !~ /^\d+$/ || $minor !~ /^\d+$/) { - bye("The x11vnc program is not installed correctly."); -} -$micro = 0 unless $micro; -my $level = $major * 100 * 100 + $minor * 100 + $micro; -my $needed = 0 * 100 * 100 + 9 * 100 + 10; -if ($level < $needed) { - bye("x11vnc version 0.9.10 or later is required. (Found version $version)"); -} - - -# 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(); - } -} - -# If there is an $allowed_users_file, check username against it: -# -if ($allowed_users_file ne '') { - if (! open(USERS, "<$allowed_users_file")) { - bye("Internal Error #0"); - } - my $ok = 0; - while (<USERS>) { - chomp; - $_ =~ s/^\s*//; - $_ =~ s/\s*$//; - next if /^#/; - if ($username eq $_) { - $ok = 1; - } - } - close USERS; - if (! $ok) { - bye("Denied Username.<p>$login_str"); - } -} - -# If there is a $denied_users_file, check username against it: -# -if ($denied_users_file ne '') { - if (! open(USERS, "<$denied_users_file")) { - bye("Internal Error #0"); - } - my $ok = 1; - while (<USERS>) { - chomp; - $_ =~ s/^\s*//; - $_ =~ s/\s*$//; - next if /^#/; - if ($username eq $_) { - $ok = 0; - } - } - close USERS; - if (! $ok) { - bye("Denied Username.<p>$login_str"); - } -} - -# 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: -# (requires x11vnc 0.9.10 or later.) -# -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 = $starting_port + $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: -# (change these if you encounter timing problems, etc.) -# -my $timeout = 75; -my $extra = ''; -if ($fixed_port) { - # settings for fixed port case: - $timeout = 45; - $extra .= " -loopbg100,1"; -} -$timeout = $x11vnc_timeout if $x11vnc_timeout ne ''; - -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: -# (disabled by default.) -# -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 = $starting_port; # default range 7000-8000. - my $pmax = $starting_port + 1000; - - 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 = ''; - if ($have_inet6 && $listen_on_ipv6) { - eval {$sock = IO::Socket::INET6->new( - Listen => 1, - LocalPort => $p, - ReuseAddr => 1, - Domain => AF_INET6, - LocalAddr => "::", - Proto => "tcp" - );}; - } else { - $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 doing 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"); - } - - # disable fifo safety if requested: - # - if ($disable_vnc_passwd_fifo_safety) { - $use_fifo = ''; - } - - # 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); - 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: - if ($have_inet6 && $listen_on_ipv6) { - eval {$sock = IO::Socket::INET6->new( - Listen => 1, - LocalPort => $vnc_port, - ReuseAddr => 1, - Domain => AF_INET6, - LocalAddr => "::", - Proto => "tcp" - );}; - } else { - $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); - - # make it smaller since we 'Open New Window' below anyway. - if ($applet_width ne 'W') { - $W = $applet_width; - } - if ($applet_height ne 'H') { - $H = $applet_height; - } - - my $tUVC = ($trustUrlVncCert ? 'yes' : 'no'); - - # see $applet_html set in defaults section for more info: - # - my $str = $applet_html; - - $str =~ s/_UID_/$uid/g; - $str =~ s/_VNC_PORT_/$vnc_port/g; - $str =~ s/_WIDTH_/$W/g; - $str =~ s/_HEIGHT_/$H/g; - $str =~ s/_PASS_/$pass/g; - $str =~ s/_APPLET_JAR_/$applet_jar/g; - $str =~ s/_APPLET_CLASS_/$applet_class/g; - $str =~ s/_TRUST_UVC_/$tUVC/g; - - if ($enable_port_redirection && $redirect_host ne '') { - $str =~ s/name=PASSWORD value=.*>/name=NOT_USED value=yes>/i; - #$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') { - if ($have_inet6 && $listen_on_ipv6) { - eval {$find_free_port = IO::Socket::INET6->new( - Listen => 1, - LocalPort => $vnc_port, - ReuseAddr => 1, - Domain => AF_INET6, - LocalAddr => "::", - Proto => "tcp" - );}; - } else { - $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': -# -# (it only applies to the vnc redirector $enable_port_redirection mode -# which is off by default.) -# -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 = ''; - my $port = ''; - if ($redirect_host =~ /^(.*):(\d+)$/) { - ($host, $port) = ($1, $2); - } - - my $sock = IO::Socket::INET->new( - PeerAddr => $host, - PeerPort => $port, - Proto => "tcp" - ); - if (! $sock && $have_inet6) { - eval {$sock = IO::Socket::INET6->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; -} |