Commit e2e93479 authored by runge's avatar runge

x11vnc: add -ssl mode using libssl. Include Xdummy in misc.

parent 5e726096
......@@ -175,7 +175,7 @@ if test "$X_CFLAGS" != "-DX_DISPLAY_MISSING"; then
esac
fi
# only x11vnc uses crypt()
# currently only x11vnc uses crypt() or libssl:
AH_TEMPLATE(HAVE_LIBCRYPT, [libcrypt library present])
AC_ARG_WITH(crypt,
[ --without-crypt disable support for libcrypt],,)
......@@ -188,6 +188,25 @@ if test "$X_CFLAGS" != "-DX_DISPLAY_MISSING"; then
$X_LIBS $X_PRELIBS -lcrypt $X_EXTRA_LIBS)
fi
fi
AH_TEMPLATE(HAVE_LIBSSL, [openssl libssl library present])
AC_ARG_WITH(ssl,
[ --without-ssl disable support for openssl libssl],,)
if test "x$with_ssl" != "xno"; then
AC_CHECK_LIB(ssl, SSL_library_init,
X_PRELIBS="$X_PRELIBS -lssl"
[AC_DEFINE(HAVE_LIBSSL) HAVE_LIBSSL="true"], ,
$X_LIBS $X_PRELIBS -lssl $X_EXTRA_LIBS)
fi
AH_TEMPLATE(HAVE_LIBCRYPTO, [openssl libcrypto library present])
AC_ARG_WITH(crypto,
[ --without-crypto disable support for openssl libcrypto],,)
# some OS's need both -lssl and -lcrypto
if test "x$HAVE_LIBSSL" = "xtrue"; then
AC_CHECK_LIB(crypto, RAND_load_file,
X_PRELIBS="$X_PRELIBS -lcrypto"
[AC_DEFINE(HAVE_LIBSSL) HAVE_LIBSSL="true"], ,
$X_LIBS $X_PRELIBS -lcrypto $X_EXTRA_LIBS)
fi
X_LIBS="$X_LIBS $X_PRELIBS -lX11 $X_EXTRA_LIBS"
......
2006-03-11 Karl Runge <runge@karlrunge.com>
* x11vnc: add -ssl mode using libssl. Include Xdummy in misc.
a few more macros for smallerfoot, etc.
2006-03-08 Karl Runge <runge@karlrunge.com>
* x11vnc: manage CLIPBOARD in addition to PRIMARY. -debug_sel
Make reverse connections require passwords. -usepw option.
......
......@@ -13,7 +13,7 @@ endif
if HAVE_X
bin_PROGRAMS=x11vnc
x11vnc_SOURCES = 8to24.c cleanup.c connections.c cursor.c gui.c help.c inet.c keyboard.c options.c pointer.c rates.c remote.c scan.c screen.c selection.c solid.c sslcmds.c unixpw.c user.c userinput.c util.c win_utils.c x11vnc.c x11vnc_defs.c xdamage.c xevents.c xinerama.c xkb_bell.c xrandr.c xrecord.c xwrappers.c 8to24.h allowed_input_t.h blackout_t.h cleanup.h connections.h cursor.h enums.h gui.h help.h inet.h keyboard.h options.h params.h pointer.h rates.h remote.h scan.h screen.h scrollevent_t.h selection.h solid.h sslcmds.h tkx11vnc.h unixpw.h user.h userinput.h util.h win_utils.h winattr_t.h x11vnc.h xdamage.h xevents.h xinerama.h xkb_bell.h xrandr.h xrecord.h xwrappers.h
x11vnc_SOURCES = 8to24.c cleanup.c connections.c cursor.c gui.c help.c inet.c keyboard.c options.c pointer.c rates.c remote.c scan.c screen.c selection.c solid.c sslcmds.c sslhelper.c unixpw.c user.c userinput.c util.c win_utils.c x11vnc.c x11vnc_defs.c xdamage.c xevents.c xinerama.c xkb_bell.c xrandr.c xrecord.c xwrappers.c 8to24.h allowed_input_t.h blackout_t.h cleanup.h connections.h cursor.h enums.h gui.h help.h inet.h keyboard.h options.h params.h pointer.h rates.h remote.h scan.h screen.h scrollevent_t.h selection.h solid.h sslcmds.h sslhelper.h tkx11vnc.h unixpw.h user.h userinput.h util.h win_utils.h winattr_t.h x11vnc.h xdamage.h xevents.h xinerama.h xkb_bell.h xrandr.h xrecord.h xwrappers.h
INCLUDES=@X_CFLAGS@
x11vnc_LDADD=$(LDADD) @X_LIBS@ $(LD_CYGIPC)
endif
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -10,6 +10,7 @@
#include "solid.h"
#include "unixpw.h"
#include "sslcmds.h"
#include "sslhelper.h"
/*
* Exiting and error handling routines
......@@ -133,6 +134,9 @@ void clean_up_exit (int ret) {
solid_bg(1);
}
stop_stunnel();
if (use_openssl) {
ssh_helper_pid(0, 0); /* killall */
}
X_LOCK;
XTestDiscard_wr(dpy);
......
......@@ -11,6 +11,8 @@
#include "screen.h"
#include "unixpw.h"
#include "scan.h"
#include "sslcmds.h"
#include "sslhelper.h"
/*
* routines for handling incoming, outgoing, etc connections
......@@ -542,6 +544,7 @@ static int accepted_client = 0;
* callback for when a client disconnects
*/
static void client_gone(rfbClientPtr client) {
ClientData *cd = NULL;
client_count--;
if (client_count < 0) client_count = 0;
......@@ -559,17 +562,27 @@ static void client_gone(rfbClientPtr client) {
}
}
if (no_autorepeat && client_count == 0) {
autorepeat(1, 0);
}
if (use_solid_bg && client_count == 0) {
solid_bg(1);
}
if (gone_cmd && *gone_cmd != '\0') {
ClientData *cd = NULL;
if (client->clientData) {
cd = (ClientData *) client->clientData;
if (cd->ssh_helper_pid > 0) {
int status;
rfbLog("sending SIGTERM to ssh_helper_pid: %d\n",
cd->ssh_helper_pid);
kill(cd->ssh_helper_pid, SIGTERM);
#if LIBVNCSERVER_HAVE_SYS_WAIT_H && LIBVNCSERVER_HAVE_WAITPID
waitpid(cd->ssh_helper_pid, &status, WNOHANG);
#endif
ssh_helper_pid(cd->ssh_helper_pid, -1); /* delete */
}
}
if (gone_cmd && *gone_cmd != '\0') {
if (strstr(gone_cmd, "popup") == gone_cmd) {
int x = -64000, y = -64000, timeout = 120;
char *userhost = ident_username(client);
......@@ -1452,6 +1465,10 @@ static int do_reverse_connect(char *str) {
rfbLog("reverse_connect: screen not setup yet.\n");
return 0;
}
if (use_openssl && !getenv("X11VNC_SSL_ALLOW_REVERSE")) {
rfbLog("reverse connections disabled in -ssl mode.\n");
return 0;
}
/* copy in to host */
host = (char *) malloc(len+1);
......@@ -1476,7 +1493,7 @@ static int do_reverse_connect(char *str) {
return 0;
}
}
if (! getenv("UNIXPW_DISABLE_STUNNEL") && ! have_ssh_env()) {
if (! getenv("UNIXPW_DISABLE_SSL") && ! have_ssh_env()) {
rfbLog("reverse_connect: in -inetd stunnel/ssh\n");
rfbLog("required under -unixpw\n");
return 0;
......@@ -1824,6 +1841,13 @@ enum rfbNewClientAction new_client(rfbClientPtr client) {
clients_served++;
if (0) fprintf(stderr, "new_client: %s %d\n", client->host, clients_served);
if (use_openssl || use_stunnel) {
if (! ssl_initialized) {
rfbLog("denying additional client: %s ssl not setup"
" yet.\n", client->host);
return(RFB_CLIENT_REFUSE);
}
}
if (unixpw && unixpw_in_progress) {
rfbLog("denying additional client: %s during -unixpw login.\n",
client->host);
......@@ -1857,6 +1881,13 @@ if (0) fprintf(stderr, "new_client: %s %d\n", client->host, clients_served);
cd->input[0] = '-';
cd->login_viewonly = -1;
cd->login_time = time(0);
cd->ssh_helper_pid = 0;
if (use_openssl && openssl_last_helper_pid) {
if (0) fprintf(stderr, "SET ssh_helper_pid: %d\n", openssl_last_helper_pid);
cd->ssh_helper_pid = openssl_last_helper_pid;
openssl_last_helper_pid = 0;
}
if (! accept_client(client)) {
rfbLog("denying client: %s local user rejected connection.\n",
......
......@@ -188,6 +188,7 @@ static int got_sigusr1 = 0;
static void sigusr1 (int sig) {
got_sigusr1 = 1;
if (0) sig = 0;
}
static void run_gui(char *gui_xdisplay, int connect_to_x11vnc, int start_x11vnc,
......
......@@ -14,7 +14,7 @@ void nopassword_warning_msg(int gotloc);
void print_help(int mode) {
#if !SMALL_FOOTPRINT
#if !SKIP_HELP
char help[] =
"\n"
"x11vnc: allow VNC connections to real X11 displays. %s\n"
......@@ -423,9 +423,9 @@ void print_help(int mode) {
" send one before a 25 second timeout. Existing clients\n"
" are view-only during this period.\n"
"\n"
" Since the detailed behavior of su(1) can vary from\n"
" OS to OS and for local configurations, please test\n"
" the mode carefully on your systems before using it.\n"
" Since the detailed behavior of su(1) can vary from OS\n"
" to OS and for local configurations, please test the mode\n"
" carefully on your systems before using it in production.\n"
" E.g. try different combinations of valid/invalid\n"
" usernames and valid/invalid passwords to see if it\n"
" behaves correctly. x11vnc will be conservative and\n"
......@@ -443,53 +443,64 @@ void print_help(int mode) {
" e.g. password aging modules. These logins will fail\n"
" as well even when the correct password is supplied.\n"
"\n"
" *IMPORTANT*: to prevent the Unix password being sent in\n"
" *clear text* over the network, two x11vnc options are\n"
" enforced: 1) -localhost and 2) -stunnel. The former\n"
" requires the viewer connection to appear to come from\n"
" the same machine x11vnc is running on (e.g. from a ssh\n"
" -L port redirection). The latter requires the -stunnel\n"
" SSL mode be used (see the description below).\n"
" **IMPORTANT**: to prevent the Unix password being sent\n"
" in *clear text* over the network, one of two schemes\n"
" will be enforced: 1) the -ssl builtin SSL mode, or 2)\n"
" require both -localhost and -stunnel be enabled.\n"
"\n"
" To override these restrictions you can set environment\n"
" variables before starting x11vnc:\n"
" Method 1) ensures the traffic is encrypted between\n"
" viewer and server. A PEM file will be required, see the\n"
" discussion under -ssl below (under some circumstances\n"
" a temporary one can be automatically generated).\n"
"\n"
" Set UNIXPW_DISABLE_STUNNEL=1 to disable using -stunnel.\n"
" Evidently you will be using a different method to\n"
" encrypt the data between the vncviewer and x11vnc:\n"
" e.g. ssh(1) or a VPN. Note that use of -localhost\n"
" with ssh(1) is roughly the same as requiring a Unix\n"
" user login (since a Unix password or the user's public\n"
" key authentication is used by ssh on the machine where\n"
" x11vnc runs and only local connections are accepted)\n"
" Method 2) requires the viewer connection to appear\n"
" to come from the same machine x11vnc is running on\n"
" (e.g. from a ssh -L port redirection). And that the\n"
" -stunnel SSL mode be used for encryption over the\n"
" network.(see the description of -stunnel below).\n"
"\n"
" As a convenience, if you ssh(1) in and start x11vnc it\n"
" will check if the environment variable SSH_CONNECTION\n"
" is set and appears reasonable. If it does, then the\n"
" stunnel requirement is dropped since it is assumed\n"
" you are using ssh for the encrypted tunnelling.\n"
" Use -stunnel to force stunnel usage for this case.\n"
" -ssl or -stunnel requirement will be dropped since it is\n"
" assumed you are using ssh for the encrypted tunnelling.\n"
" -localhost is still enforced. Use -ssl or -stunnel to\n"
" force SSL usage for this case.\n"
"\n"
" To override these restrictions you can set environment\n"
" variables before starting x11vnc:\n"
"\n"
" Set UNIXPW_DISABLE_SSL=1 to disable requiring either\n"
" -ssl or -stunnel. Evidently you will be using a\n"
" different method to encrypt the data between the\n"
" vncviewer and x11vnc: e.g. ssh(1) or a VPN. Note that\n"
" use of -localhost with ssh(1) is roughly the same as\n"
" requiring a Unix user login (since a Unix password or\n"
" the user's public key authentication is used by sshd on\n"
" the machine where x11vnc runs and only local connections\n"
" are accepted)\n"
"\n"
" Set UNIXPW_DISABLE_LOCALHOST=1 to disable the -localhost\n"
" requirement. One should never do this (i.e. allow the\n"
" Unix passwords to be sniffed on the network).\n"
" requirement in Method 2). One should never do this\n"
" (i.e. allow the Unix passwords to be sniffed on the\n"
" network).\n"
"\n"
" Regarding reverse connections (e.g. -R connect:host),\n"
" the -localhost constraint is in effect and the reverse\n"
" if the -localhost constraint is in effect then reverse\n"
" connections can only be used to connect to the same\n"
" machine x11vnc is running on (default port 5500).\n"
" Please use a ssh or stunnel port redirection to the\n"
" viewer machine to tunnel the reverse connection over\n"
" an encrypted channel. Note that Unix username and\n"
" password *will* be prompted for (unlike VNC passwords\n"
" that are skipped for reverse connections).\n"
" an encrypted channel. Note that in -ssl mode reverse\n"
" connection are disabled.\n"
"\n"
" NOTE: in -inetd mode the two settings are attempted\n"
" to be enforced for reverse connections. Be sure to\n"
" XXX -inetd + -ssl\n"
" In -inetd mode the two settings are attempted to be\n"
" enforced for reverse connections. Be sure to also\n"
" use encryption from the viewer to inetd since x11vnc\n"
" cannot guess easily if it is encrpyted. Note: you can\n"
" cannot guess easily if it is encrpyted. Tip: you can\n"
" also have your own stunnel spawn x11vnc in -inetd mode\n"
" (i.e. bypassing inetd). See the FAQ.\n"
" (i.e. bypassing inetd). See the FAQ for details.\n"
"\n"
" The user names in the comma separated [list] can have\n"
" per-user options after a \":\", e.g. \"fred:opts\"\n"
......@@ -521,17 +532,84 @@ void print_help(int mode) {
" other environment. All of the -unixpw options and\n"
" contraints apply.\n"
"\n"
"-stunnel [pem] Use the stunnel(1) (www.stunnel.org) to provide\n"
"-ssl [pem] Use the openssl library (www.openssl.org) to provide a\n"
" built-in encrypted SSL tunnel between VNC viewers and\n"
" x11vnc. This requires libssl support to be compiled\n"
" into x11vnc at build time. If x11vnc is not built\n"
" with libssl support it will exit immediately when -ssl\n"
" is prescribed.\n"
"\n"
" [pem] is optional, use \"-ssl /path/to/mycert.pem\" to\n"
" specify a PEM certificate file to use to identify and\n"
" provide a key for this server.\n"
"\n"
" Connecting VNC viewer SSL tunnels can authenticate\n"
" this server if they have the public key part of the\n"
" certificate (or a common certificate authority, CA,\n"
" verifies this server's cert). This is used to prevent\n"
" man-in-the-middle attacks. Otherwise, if the VNC viewer\n"
" accepts this server's key without verification, at\n"
" least the traffic is protected from passive sniffing\n"
" on the network.\n"
"\n"
" If [pem] is not supplied and the openssl(1) utility\n"
" command exists in PATH, then a temporary, self-signed\n"
" certificate will be generated for this session (this\n"
" may take 5-20 seconds on slow machines). If openssl(1)\n"
" cannot be used to generate a temporary certificate\n"
" x11vnc exits immediately.\n"
"\n"
" If successful in using openssl(1) to generate a\n"
" certificate, the public part of it will be displayed\n"
" to stdout (e.g. one could copy it to the client-side\n"
" to provide authentication of the server to VNC viewers.)\n"
"\n"
" Set the env. var. X11VNC_SHOW_TMP_PEM=1 to have x11vnc\n"
" print out the entire certificate, including the PRIVATE\n"
" KEY part, to stderr. One could reuse this cert if saved\n"
" in a [pem] file. Similarly, set X11VNC_KEEP_TMP_PEM=1\n"
" to not delete the temporary PEM file: the file name\n"
" will be printed to stderr (so one could move it to a\n"
" safe place for reuse).\n"
"\n"
" Reverse connections are disabled in -ssl\n"
" mode because the data cannot be encrypted.\n"
" Set X11VNC_SSL_ALLOW_REVERSE=1 to override this.\n"
"\n"
" Your VNC viewer will also need to be able to connect\n"
" via SSL. See the discussion below under -stunnel and\n"
" the FAQ for how this might be achieved. E.g. on Unix it\n"
" is easy to write a shell script that starts up stunnel\n"
" and then vncviewer.\n"
"\n"
"-sslverify [path] For either of the -ssl or -stunnel modes, use [path]\n"
" to provide certificates to authenticate incoming VNC\n"
" client connections. This can be used as a method to\n"
" replace standard password authentication.\n"
"\n"
" If [path] is a directory it contains the client (or CA)\n"
" certificates in separate files. If [path] is a file, it\n"
" contains multiple certificates. These correspond to the\n"
" \"CApath = dir\" and \"CAfile = file\" stunnel options.\n"
" See the stunnel(8) manpage for details.\n"
"\n"
" To create certificates for all sorts of authentications\n"
" (clients, servers, via CA, etc) see the openssl(1)\n"
" command. Of particular usefulness is the x509\n"
" subcommand of openssl(1).\n"
"\n"
"-stunnel [pem] Use the stunnel(8) (www.stunnel.org) to provide\n"
" an encrypted SSL tunnel between viewers and x11vnc.\n"
" This requires stunnel to be installed on the system and\n"
" available via PATH (n.b. stunnel is often installed in\n"
" sbin directories). Version 4.x of stunnel is assumed;\n"
" see -stunnel3 below.\n"
" sbin directories). Version 4.x of stunnel is assumed\n"
" (but see -stunnel3 below.)\n"
"\n"
" [pem] is optional, use \"-stunnel /path/to/stunnel.pem\"\n"
" to specify a PEM certificate file to pass to stunnel.\n"
" Whether one is needed or not depends on your stunnel\n"
" configuration.\n"
" configuration. stunnel often generates one at install\n"
" time.\n"
"\n"
" stunnel is started up as a child process of x11vnc and\n"
" any SSL connections stunnel receives are decrypted and\n"
......@@ -543,14 +621,15 @@ void print_help(int mode) {
" avoid people routing around the SSL channel. Set\n"
" STUNNEL_DISABLE_LOCALHOST=1 to disable the requirement.\n"
"\n"
" Your VNC viewer will need to be able to connect via SSL.\n"
" Unfortunately not too many do this. UltraVNC seems to\n"
" have a SSL plugin. It is not too difficult to set up\n"
" an stunnel or other SSL tunnel on the viewer side.\n"
" Your VNC viewer will also need to be able to connect\n"
" via SSL. Unfortunately not too many do this. UltraVNC\n"
" seems to have an encryption plugin. It is not too\n"
" difficult to set up an stunnel or other SSL tunnel on\n"
" the viewer side.\n"
"\n"
" A simple example on Unix using stunnel 3.x is:\n"
"\n"
" %% stunnel -c -d localhost:5901 -r remote:5900\n"
" %% stunnel -c -d localhost:5901 -r remotehost:5900\n"
" %% vncviewer localhost:1\n"
"\n"
" For Windows, stunnel has been ported to it and there\n"
......@@ -2175,22 +2254,24 @@ void print_help(int mode) {
" debug_xdamage debug_wireframe nodebug_wireframe\n"
" debug_wireframe debug_scroll nodebug_scroll debug_scroll\n"
" debug_tiles dbt nodebug_tiles nodbt debug_tiles\n"
" debug_grabs nodebug_grabs dbg nodbg noremote\n"
" debug_grabs nodebug_grabs debug_sel nodebug_sel dbg\n"
" nodbg noremote\n"
"\n"
" aro= noop display vncdisplay desktopname guess_desktop\n"
" http_url auth xauth users rootshift clipshift\n"
" scale_str scaled_x scaled_y scale_numer scale_denom\n"
" scale_fac scaling_blend scaling_nomult4 scaling_pad\n"
" scaling_interpolate inetd privremote unsafe safer\n"
" nocmds passwdfile unixpw unixpw_nis unixpw_list stunnel\n"
" stunnel_pem using_shm logfile o flag 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 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"
" nocmds passwdfile unixpw unixpw_nis unixpw_list ssl\n"
" ssl_pem sslverify stunnel stunnel_pem usepw using_shm\n"
" logfile o flag rc norc h help V version lastmod bg\n"
" sigpipe threads readrate netrate netlatency pipeinput\n"
" clients client_count pid ext_xtest ext_xtrap ext_xrecord\n"
" ext_xkb ext_xshm ext_xinerama ext_overlay ext_xfixes\n"
" ext_xdamage ext_xrandr rootwin num_buttons button_mask\n"
" mouse_x mouse_y bpp depth indexed_color dpy_x dpy_y\n"
" wdpy_x wdpy_y off_x off_y cdpy_x cdpy_y coff_x coff_y\n"
" 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"
......
#!/bin/sh
#
# Xdummy: an LD_PRELOAD hack to run a stock XFree86(1) or Xorg(1) server
# with the "dummy" video driver to make it avoid Linux VT switching, etc.
#
# Run "Xdummy -help" for more info.
#
xserver=""
geom=""
install=""
uninstall=""
root=1
debug=""
strace=""
runit=1
prconf=""
noconf=""
PATH=$PATH:/bin:/usr/bin
export PATH
program=`basename "$0"`
help () {
cat << END
$program: a hack to run a stock XFree86(1) or Xorg(1) server with the
"dummy" video driver such that it AVOIDS the Linux VT switching, keyboard
mouse conflicts, etc associated with normal use of "dummy".
In other words, try to make XFree86/Xorg with the Device "dummy" driver
act more like Xvfb(1).
To achieve this, while running the real Xserver $program intercepts system
and library calls via the LD_PRELOAD method and modifies the behavior
to make it work correctly (i.e. avoid the VT stuff). LD_PRELOAD tricks
are usually "clever hacks" and so might not work in all circumstances.
The primary motivation for the Xdummy script is to provide a virtual X
server for x11vnc but with more features than Xvfb (or Xvnc), however
it could be used for other reasons (e.g. better automated testing than
with Xvfb).
Currently this program needs to be run as root, since it is too difficult
to trick it otherwise. Hopefully this will be relaxed at a later date
if the needed tricks are discovered.
Also, gcc/cc is required to compile the LD_PRELOAD shared object.
See -install and -uninstall described below.
Usage:
$program <${program}-args> [--] <Xserver-args>
Examples:
$program :1
$program -debug -tmpdir ~/mytmp :1
$program -install
startx example:
startx -e bash -- $program :2 -depth 16
(startx needs to be run as root, you can su(1) to a normal user
in the bash shell and then launch ~/.xinitrc or ~/.xsession,
gnome-session, startkde, startxfce4, etc.)
xdm example:
xdm -config /usr/local/dummy/xdm-config -nodaemon
where the xdm-config file has line:
DisplayManager.servers: /usr/local/dummy/Xservers
and /usr/local/dummy/Xservers has lines:
:1 local /usr/local/dummy/Xdummy :1 -debug
:2 local /usr/local/dummy/Xdummy :2 -debug
gdm/kdm example:
TBD.
Options:
${program}-args:
-install Compile the LD_PRELOAD shared object and install it
next to the $program script file as: $0.so
When that file exists it is used as the LD_PRELOAD
shared object without recompiling. Otherwise,
each time $program is run the LD_PRELOAD shared
object is compiled as a file in /tmp (or -tmpdir).
-uninstall Remove the file: $0.so
The LD_PRELOAD shared object will then be compiled
each time this program is run.
Note: the X server is not started under -install or -uninstall.
:N The DISPLAY can be the first $program argument.
It is passed to the real X server. This is to
aid use with startx(1), xinit(1), xdm(1), etc.
-geom geom1[,geom2...] Take the geometry (e.g. 1024x768) or
list of geometries and insert them into the
Screen section of the tweaked X server
config file. Use this to have a smaller geometry
than the one in the system config file.
-tmpdir dir Specify a temporary directory, owned by you and
only writable by you. This is used in place of
/tmp/Xdummy.\$USER/ to placed the $program.so
shared object, tweaked config files, etc.
-nonroot Try to run in non-root mode (XXX NOT yet working).
-xserver path Specify the path to the Xserver to use. Default
is to try "Xorg" first and then "XFree86". If
those are not in \$PATH, use these locations:
/usr/X11R6/bin/Xorg
/usr/X11R6/bin/XFree86
-n Do not run the command to start the X server,
just show the command that $program would run.
The LD_PRELOAD shared object will be built,
if needed.
-prconf Print, to stdout, the tweaked Xorg or XFree86
config file (-config and -xf86config server
options, respectively). The Xserver is not
started.
-noconf Do not tweak the Xorg or XFree86 config file
(system or server command line) at all. It is
up to you to make sure it is a working config
file (e.g. "dummy" driver, etc). Perhaps you
want to use a file based on -prconf output.
-debug Extra debugging output.
-strace strace(1) the Xserver process (for debugging).
-h, -help Print out this help.
Xserver-args:
Most of the XFree86 and Xorg options will work. Important ones
that may be supplied if missing:
:N X Display number for server to use.
vtNN Linux virtual terminal (VT) to use (a VT is currently
still used, just not switched to and from).
-config file Driver "dummy" tweaked config file, a
-xf86config file number of settings are tweaked besides Driver.
If -config/-xf86config is not given, the system one is used.
Any settings in the config file that are not consistent with
"dummy" mode will be overwritten (unless -noconf is used).
Notes:
The XFree86/Xorg "dummy" driver is currently undocmented. It works
well in this mode, but it is evidently not intended for end users.
If the display Xserver-arg (e.g. :1) is not given, or ":" or ":9999"
is given that indicates $program should try to find a free one.
If the display virtual terminal, VT, (e.g. vt9) is not given that
indicates $program should try to find a free one (or guess a high one).
This program is not completely secure WRT files in /tmp (but it
tries to some degree). Better is to use the -tmpdir option to supply a
directory only writable by you. Even better is to get rid of users on
the local machine you do not trust :-)
END
}
warn() {
echo "$*" 1>&2
}
#warn "id: `id -u`"
# See if it needs to be run as root:
if [ "X$XDUMMY_SU_EXEC" = "X" -a "X`id -u`" != "X0" ]; then
dosu=1
XDUMMY_SU_EXEC=1
export XDUMMY_SU_EXEC
for arg in $*
do
if [ "X$arg" = "X-nonroot" ]; then
dosu=""
elif [ "X$arg" = "X-help" ]; then
dosu=""
elif [ "X$arg" = "X-h" ]; then
dosu=""
elif [ "X$arg" = "X-install" ]; then
dosu=""
elif [ "X$arg" = "X-uninstall" ]; then
dosu=""
elif [ "X$arg" = "X-n" ]; then
dosu=""
elif [ "X$arg" = "X-prconf" ]; then
dosu=""
elif [ "X$arg" = "X--" ]; then
break
fi
done
if [ $dosu ]; then
warn "$program: currently needs to be run as root to work."
warn "$program: supply the root password to restart as root:"
exec su -c "$0 $*"
exit
fi
fi
#warn "args: $*"
disp=""
# Process Xdummy args:
while [ "X$1" != "X" ]
do
case $1 in
":"*) disp=$1
;;
"-install") install=1
;;
"-uninstall") uninstall=1
;;
"-nonroot") root=""
;;
"-xserver") xserver="$2"; shift
;;
"-geom"*) geom="$2"; shift
;;
"-tmpdir") XDUMMY_TMPDIR="$2"; shift
;;
"-n") runit=""
;;
"-no") runit=""
;;
"-prconf") prconf=1
;;
"-noconf") noconf=1
;;
"-debug") debug=1
;;
"-nodebug") debug=""
;;
"-strace") strace=1
;;
"-h"*) help; exit 0
;;
"--") shift; break
;;
*) break
;;
esac
shift
done
# Try to get a username for use in our tmp directory, etc.
user=""
if [ X`id -u` = "X0" ]; then
user=root # this will also be used below for id=0
elif [ "X$USER" != "X" ]; then
user=$USER
elif [ "X$LOGNAME" != "X" ]; then
user=$LOGNAME
fi
# keep trying...
if [ "X$user" = "X" ]; then
user=`whoami 2>/dev/null`
fi
if [ "X$user" = "X" ]; then
user=`basename $HOME`
fi
if [ "X$user" = "X" -o "X$user" = "X." ]; then
user="u$$"
fi
# Function to compile the LD_PRELOAD shared object:
make_so() {
# extract code embedded in this script into a tmp C file:
n1=`grep -n '^#code_begin' $0 | head -1 | awk -F: '{print $1}'`
n2=`grep -n '^#code_end' $0 | head -1 | awk -F: '{print $1}'`
n1=`expr $n1 + 1`
dn=`expr $n2 - $n1`
tmp=$tdir/Xdummy.$$.c
rm -f $tmp
if [ -e $tmp -o -h $tmp ]; then
warn "$tmp still exists."
exit 1
fi
tail +$n1 $0 | head -$dn > $tmp
# compile it to Xdummy.so:
rm -f $SO
touch $SO
if [ ! -f $SO ]; then
SO=$tdir/Xdummy.$user.so
warn "warning switch LD_PRELOAD shared object to: $SO"
fi
rm -f $SO
# we assume gcc:
cc -shared -fPIC -o $SO $tmp
rc=$?
rm -f $tmp
if [ $rc != 0 ]; then
warn "$program: cannot build $SO"
exit 1
fi
if [ "X$debug" != "X" -o "X$install" != "X" ]; then
warn "$program: created $SO"
ls -l "$SO"
fi
}
# Set tdir to tmp dir for make_so():
if [ "X$XDUMMY_TMPDIR" != "X" ]; then
tdir=$XDUMMY_TMPDIR
mkdir -p $tdir
else
tdir="/tmp"
fi
# Handle -install/-uninstall case:
SO=$0.so
if [ "X$install" != "X" -o "X$uninstall" != "X" ]; then
if [ -e $SO -o -h $SO ]; then
warn "removing $SO"
fi
rm -f $SO
if [ -e $SO -o -h $SO ]; then
# not good...
warn "warning: $SO still exists."
exit 1
fi
if [ $install ]; then
make_so
if [ ! -f $SO ]; then
exit 1
fi
fi
exit 0
fi
# We need a tmp directory for the .so, tweaked config file, and for
# redirecting filenames we cannot create (under -nonroot, not yet
# working).
#
if [ "X$XDUMMY_TMPDIR" = "X" ]; then
XDUMMY_TMPDIR="/tmp/Xdummy.$user"
fi
tmp=$XDUMMY_TMPDIR
if echo "$tmp" | grep '^/tmp' > /dev/null; then
if [ "X$tmp" != "X/tmp" -a "X$tmp" != "X/tmp/" ]; then
# clean this subdir of /tmp out, otherwise leave it...
rm -rf $XDUMMY_TMPDIR
if [ -e $XDUMMY_TMPDIR ]; then
warn "$XDUMMY_TMPDIR still exists"
exit 1
fi
fi
fi
mkdir -p $XDUMMY_TMPDIR
chmod 700 $XDUMMY_TMPDIR
# see if we can write something there:
tfile="$XDUMMY_TMPDIR/test.file"
touch $tfile
if [ ! -f $tfile ]; then
XDUMMY_TMPDIR="/tmp/Xdummy.$$.$USER"
warn "warning: setting tmpdir to $XDUMMY_TMPDIR ..."
rm -rf $XDUMMY_TMPDIR || exit 1
mkdir -p $XDUMMY_TMPDIR || exit 1
fi
rm -f $tfile
export XDUMMY_TMPDIR
# compile the LD_PRELOAD shared object if needed:
if [ ! -f $SO ]; then
SO="$XDUMMY_TMPDIR/Xdummy.so"
make_so
fi
# decide which X server to use:
if [ "X$xserver" = "X" ]; then
if type Xorg >/dev/null 2>&1; then
xserver="Xorg"
elif type XFree86 >/dev/null 2>&1; then
xserver="XFree86"
elif -x /usr/X11R6/bin/Xorg; then
xserver="/usr/X11R6/bin/Xorg"
elif -x /usr/X11R6/bin/XFree86; then
xserver="/usr/X11R6/bin/XFree86"
fi
if [ "X$xserver" = "X" ]; then
# just let it fail below.
xserver="/usr/X11R6/bin/Xorg"
warn "$program: cannot locate a stock Xserver... assuming $xserver"
fi
fi
# see if the binary is suid or not readable under -nonroot (XXX not yet useful):
xserver_path=`type -p $xserver 2>/dev/null`
if [ -e "$xserver_path" -a "X$root" = "X" -a "X$runit" != "X" ]; then
if [ ! -r $xserver_path -o -u $xserver_path ]; then
# XXX not quite correct with rm -rf $XDUMMY_TMPDIR ...
base=`basename "$xserver_path"`
new="$tdir/$base.$user"
if [ ! -e $new ]; then
warn "need to copy $xserver_path to $new as root:"
ls -l $xserver_path 1>&2
warn "please supply root passwd to 'su -c'"
touch $new || exit 1
chmod 700 $new || exit 1
su -c "cat $xserver_path > $new"
elif [ ! -O $new ]; then
warn "file \"$new\" not owned by us!"
ls -l $new
exit 1
fi
xserver=$new
fi
fi
# work out display:
if [ "X$disp" != "X" ]; then
:
elif [ "X$1" != "X" ]; then
if echo "$1" | grep '^:[0-9]' > /dev/null; then
disp=$1
shift
elif [ "X$1" = "X:" ]; then
# ":" means for us to find one.
shift
fi
fi
if [ "X$disp" = "X" -o "X$disp" = "X:" ]; then
# try to find an open display port:
ports=`netstat -ant | grep LISTEN | awk '{print $4}' | sed -e 's/^.*://'`
n=0
while [ $n -le 20 ]
do
port=`printf "60%02d" $n`
if echo "$ports" | grep "^${port}\$" > /dev/null; then
:
else
disp=":$n"
warn "$program: auto-selected DISPLAY $disp"
break
fi
n=`expr $n + 1`
done
fi
# work out which vt to use, try to find an open one if necessary.
vt=""
for arg in $*
do
if echo "$arg" | grep '^vt' > /dev/null; then
vt=$arg
break
fi
done
if [ "X$vt" = "X" ]; then
if [ "X$user" = "Xroot" ]; then
# root can user fuser(1) to see if it is in use:
if type fuser >/dev/null 2>&1; then
# try /dev/tty17 thru /dev/tty32
n=17
while [ $n -le 32 ]
do
dev="/dev/tty$n"
if fuser $dev >/dev/null 2>&1; then
:
else
vt="vt$n"
warn "$program: auto-selected VT $vt => $dev"
break
fi
n=`expr $n + 1`
done
fi
fi
if [ "X$vt" = "X" ]; then
# take a wild guess...
vt=vt16
warn "$program: selected VT $vt"
fi
else
vt=""
fi
# decide flavor of Xserver:
stype=`basename "$xserver"`
if echo "$stype" | grep -i xorg > /dev/null; then
stype=xorg
else
stype=xfree86
fi
# work out config file and tweak it.
next=""
config=""
got_config=""
for arg in $*
do
if [ $next ]; then
config="$arg"
got_config=1
break
fi
if [ "X$arg" = "X-xf86config" ]; then
stype="xfree86"
next=1
elif [ "X$arg" = "X-config" ]; then
stype="xorg"
next=1
fi
done
tweak_config() {
in="$1"
config2="$XDUMMY_TMPDIR/xconfig"
if [ "X$disp" != "X" ]; then
d=`echo "$disp" | sed -e 's,/,,g' -e 's/:/_:/g'`
config2="$config2$d"
fi
# perl script to tweak the config file... add/delete options, etc.
XDUMMY_GEOM=$geom; export XDUMMY_GEOM
perl > $config2 < $in -e '
$n = 0;
$geom = $ENV{XDUMMY_GEOM};
if ($geom ne "") {
$tmp = "";
foreach $g (split(/,/, $geom)) {
$tmp .= "\"$g\" ";
}
$tmp =~ s/\s*$//;
$geom = $tmp;
}
while (<>) {
$n++;
if (/^\s*#/) {
# pass comments straight thru
print;
next;
}
if (/^\s*Section\s+(\S+)/i) {
# start of Section
$sect = $1;
$sect =~ s/\W//g;
$sect =~ y/A-Z/a-z/;
$sects{$sect} = 1;
print;
next;
}
if (/^\s*EndSection/) {
# end of Section
if ($sect eq "serverflags") {
if (!$got_DontVTSwitch) {
print " ##Xdummy:##\n";
print " Option \"DontVTSwitch\" \"true\"\n";
}
if (!$got_AllowMouseOpenFail) {
print " ##Xdummy:##\n";
print " Option \"AllowMouseOpenFail\" \"true\"\n";
}
if (!$got_PciForceNone) {
print " ##Xdummy:##\n";
print " Option \"PciForceNone\" \"true\"\n";
}
} elsif ($sect eq "device") {
if (!$got_Driver) {
print " ##Xdummy:##\n";
print " Driver \"dummy\"\n";
}
if (!$got_VideoRam) {
print " ##Xdummy:##\n";
print " VideoRam 16000\n";
}
}
$sect = "";
print;
next;
}
$l = $_;
$l =~ s/#.*$//;
if ($sect eq "serverflags") {
if ($l =~ /^\s*Option.*DontVTSwitch/i) {
$_ =~ s/false/true/ig;
$got_DontVTSwitch = 1;
}
if ($l =~ /^\s*Option.*AllowMouseOpenFail/i) {
$_ =~ s/false/true/ig;
$got_AllowMouseOpenFail = 1;
}
if ($l =~ /^\s*Option.*PciForceNone/i) {
$_ =~ s/false/true/ig;
$got_PciForceNone= 1;
}
}
if ($sect eq "module") {
if ($l =~ /^\s*Load.*\b(dri|fbdevhw)\b/i) {
$_ = "##Xdummy## $_";
}
}
if ($sect eq "device") {
if ($l =~ /^(\s*Driver)\b/i) {
$_ = "$1 \"dummy\"\n";
$got_Driver = 1;
}
if ($l =~ /^\s*VideoRam/i) {
$got_VideoRam= 1;
}
}
if ($sect eq "inputdevice") {
if ($l =~ /^\s*Option.*\bDevice\b/i) {
print " ##Xdummy:##\n";
$_ = " Option \"Device\" \"/dev/dilbert$n\"\n";
}
}
if ($sect eq "screen") {
if ($geom ne "") {
if ($l =~ /^(\s*Modes)\b/i) {
$_ = "$1 $geom\n";
print " ##Xdummy:##\n";
$got_Modes = 1;
}
}
}
print;
}
# create any crucial sections that are missing:
if (! exists($sects{serverflags})) {
print "\n##Xdummy:##\n";
print "Section \"ServerFlags\"\n";
print " Option \"DontVTSwitch\" \"true\"\n";
print " Option \"AllowMouseOpenFail\" \"true\"\n";
print " Option \"PciForceNone\" \"true\"\n";
print "EndSection\n";
}
if (! exists($sects{device})) {
print "\n##Xdummy:##\n";
print "Section \"Device\"\n";
print " Identifier \"Videocard0\"\n";
print " Driver \"dummy\"\n";
print " VideoRam 16000\n";
print "EndSection\n";
}
if (! exists($sects{monitor})) {
print "\n##Xdummy:##\n";
print "Section \"Monitor\"\n";
print " Identifier \"Monitor0\"\n";
print " HorizSync 30.0 - 130.0\n";
print " VertRefresh 50.0 - 250.0\n";
print "EndSection\n";
}
if (! exists($sects{screen})) {
print "\n##Xdummy:##\n";
print "Section \"Screen\"\n";
print " Identifier \"Screen0\"\n";
print " Device \"Videocard0\"\n";
print " Monitor \"Monitor0\"\n";
print " DefaultDepth 16\n";
print " SubSection \"Display\"\n";
print " Viewport 0 0\n";
print " Depth 16\n";
print " Modes \"1024x768\" \"800x600\" \"640x480\"\n";
print " EndSubSection\n";
print "EndSection\n";
}
';
}
args="$*"
if [ ! $noconf ]; then
# tweaked config will be put in $config2:
config2=""
if [ "X$config" != "X" ]; then
if [ ! -f $config ]; then
config="/etc/X11/$config"
fi
else
# use the default one:
if [ "X$stype" = "Xxorg" ]; then
config=/etc/X11/xorg.conf
else
if [ -f "/etc/X11/XF86Config-4" ]; then
config="/etc/X11/XF86Config-4"
else
config="/etc/X11/XF86Config"
fi
fi
if [ ! -f $config ]; then
for c in /etc/X11/xorg.conf /etc/X11/XF86Config-4 /etc/X11/XF86Config
do
if [ -f $c ]; then
config=$c
break
fi
done
fi
fi
if [ -f $config ]; then
tweak_config $config
fi
# now we need to get our tweaked config file onto the command line:
if [ ! $got_config ]; then
# append:
if [ "X$stype" = "Xxorg" ]; then
args="$args -config FUBAR"
else
args="$args -xf86config FUBAR"
fi
fi
if [ "X$config2" != "X" ]; then
# or modify $args:
args=`echo "$args" | sed \
-e "s,-config *[^ ][^ ]*,-config $config2,g" \
-e "s,-xf86config *[^ ][^ ]*,-xf86config $config2,g"`
fi
fi
if [ $prconf ]; then
warn ""
warn "The Xorg/XFree86 server config file is:"
warn ""
if [ "X$config2" = "X" ]; then
warn "NO CONFIG GENERATED."
else
cat "$config2"
fi
exit
fi
if [ $debug ]; then
XDUMMY_DEBUG=1
export XDUMMY_DEBUG
fi
if [ $root ]; then
XDUMMY_ROOT=1
export XDUMMY_ROOT
fi
# finally, run it:
if [ "X$debug" != "X" -o "X$runit" = "X" ]; then
warn ""
warn "The command to run is:"
warn ""
so=$SO
pwd=`pwd`
if echo "$so" | grep '^\./' > /dev/null; then
so=`echo "$so" | sed -e "s,^\.,$pwd,"`
fi
if echo "$so" | grep '/' > /dev/null; then
:
else
so="$pwd/$so"
fi
warn "env LD_PRELOAD=$so $xserver $disp $args $vt"
warn ""
if [ ! $runit ]; then
exit 0
fi
fi
if [ $strace ]; then
strace -f env LD_PRELOAD=$SO $xserver $disp $args $vt
else
exec env LD_PRELOAD=$SO $xserver $disp $args $vt
fi
exit $?
#########################################################################
code() {
#code_begin
#include <stdio.h>
#define O_ACCMODE 0003
#define O_RDONLY 00
#define O_WRONLY 01
#define O_RDWR 02
#define O_CREAT 0100 /* not fcntl */
#define O_EXCL 0200 /* not fcntl */
#define O_NOCTTY 0400 /* not fcntl */
#define O_TRUNC 01000 /* not fcntl */
#define O_APPEND 02000
#define O_NONBLOCK 04000
#define O_NDELAY O_NONBLOCK
#define O_SYNC 010000
#define O_FSYNC O_SYNC
#define O_ASYNC 020000
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <linux/vt.h>
#include <linux/kd.h>
#define __USE_GNU
#include <dlfcn.h>
static char tmpdir[1024];
static char str1[1024];
static char str2[1024];
static char devs[256][1024];
static int debug = -1;
static int root = -1;
void check_debug(void) {
if (debug < 0) {
if (getenv("XDUMMY_DEBUG") != NULL) {
debug = 1;
} else {
debug = 0;
}
putenv("LD_PRELOAD=");
}
}
void check_root(void) {
if (root < 0) {
if (getenv("XDUMMY_ROOT") != NULL) {
root = 1;
} else {
root = 0;
}
}
}
#define CHECKIT if (debug < 0) check_debug(); \
if (root < 0) check_root();
static void set_tmpdir(void) {
char *s;
static int didset = 0;
if (didset) {
return;
}
s = getenv("XDUMMY_TMPDIR");
if (! s) {
s = "/tmp";
}
tmpdir[0] = '\0';
strcat(tmpdir, s);
strcat(tmpdir, "/");
didset = 1;
}
static char *tmpdir_path(const char *path) {
char *str;
set_tmpdir();
strcpy(str2, path);
str = str2;
while (*str) {
if (*str == '/') {
*str = '_';
}
str++;
}
strcpy(str1, tmpdir);
strcat(str1, str2);
return str1;
}
int open(const char *pathname, int flags, unsigned short mode) {
int fd;
char *store_dev = NULL;
static int (*real_open)(const char *, int , unsigned short) = NULL;
CHECKIT
if (! real_open) {
real_open = (int (*)(const char *, int , unsigned short))
dlsym(RTLD_NEXT, "open");
}
if (! root) {
if (!strcmp(pathname, "/dev/mem")) {
;
} else if (!strcmp(pathname, "/dev/tty")) {
;
} else if (strstr(pathname, "/dev") == pathname) {
store_dev = strdup(pathname);
pathname = tmpdir_path(pathname);
if (debug) fprintf(stderr, "OPEN: -> %s\n", pathname, mode);
fd = real_open(pathname, O_WRONLY|O_CREAT, 0777);
close(fd);
}
}
fd = real_open(pathname, flags, mode);
if (debug) fprintf(stderr, "OPEN: %s %d %d fd=%d\n",
pathname, flags, mode, fd);
if (! root) {
if (store_dev && fd < 256) {
if (fd < 256) {
strcpy(devs[fd], store_dev);
}
free(store_dev);
}
}
return(fd);
}
int open64(const char *pathname, int flags, unsigned short mode) {
int fd;
CHECKIT
if (debug) fprintf(stderr, "OPEN64: %s %d %d\n", pathname, flags, mode);
fd = open(pathname, flags, mode);
return(fd);
}
FILE *fopen(const char *pathname, const char *mode) {
static FILE* (*real_fopen)(const char *, const char *) = NULL;
char *str;
CHECKIT
if (! real_fopen) {
real_fopen = (FILE* (*)(const char *, const char *))
dlsym(RTLD_NEXT, "fopen");
}
if (debug) fprintf(stderr, "FOPEN: %s %s\n", pathname, mode);
if (root) {
return(real_fopen(pathname, mode));
}
str = (char *) pathname;
if (strstr(pathname, "/var/log") == pathname) {
str = tmpdir_path(pathname);
if (debug) fprintf(stderr, "FOPEN: -> %s\n", str, mode);
}
}
#define RETURN0 if (debug) \
{fprintf(stderr, "IOCTL: covered %d 0x%x\n", fd, req);} return 0;
#define RETURN1 if (debug) \
{fprintf(stderr, "IOCTL: covered %d 0x%x\n", fd, req);} return -1;
int ioctl(int fd, int req, void *ptr) {
static int closed_xf86Info_consoleFd = 0;
static int (*real_ioctl)(int, int , void *) = NULL;
CHECKIT
if (! real_ioctl) {
real_ioctl = (int (*)(int, int , void *))
dlsym(RTLD_NEXT, "open");
}
if (debug) fprintf(stderr, "IOCTL: %d 0x%x %p\n", fd, req, ptr);
/* based on xorg-x11-6.8.1-dualhead.patch */
if (req == VT_GETMODE) {
/* close(xf86Info.consoleFd) */
if (0 && ! closed_xf86Info_consoleFd) {
/* I think better not to close it... */
close(fd);
closed_xf86Info_consoleFd = 1;
}
RETURN0
} else if (req == VT_SETMODE) {
RETURN0
} else if (req == VT_GETSTATE) {
RETURN0
} else if (req == KDSETMODE) {
RETURN0
} else if (req == KDSETLED) {
RETURN0
} else if (req == KDGKBMODE) {
RETURN0
} else if (req == VT_ACTIVATE) {
RETURN0
} else if (req == VT_WAITACTIVE) {
RETURN0
} else if (req == VT_RELDISP) {
if (ptr == (void *) 1) {
RETURN1
} else if (ptr == (void *) VT_ACKACQ) {
RETURN0
}
}
return(real_ioctl(fd, req, ptr));
}
typedef void (*sighandler_t)(int);
#define SIGUSR1 10
#define SIG_DFL ((sighandler_t)0)
sighandler_t signal(int signum, sighandler_t handler) {
static sighandler_t (*real_signal)(int, sighandler_t) = NULL;
CHECKIT
if (! real_signal) {
real_signal = (sighandler_t (*)(int, sighandler_t))
dlsym(RTLD_NEXT, "signal");
}
if (debug) fprintf(stderr, "SIGNAL: %d %p\n", signum, handler);
if (signum == SIGUSR1) {
if (debug) fprintf(stderr, "SIGNAL: skip SIGUSR1\n");
return SIG_DFL;
}
return(real_signal(signum, handler));
}
int close(int fd) {
static int (*real_close)(int) = NULL;
CHECKIT
if (! real_close) {
real_close = (int (*)(int)) dlsym(RTLD_NEXT, "close");
}
if (debug) fprintf(stderr, "CLOSE: %d\n", fd);
if (! root) {
if (fd < 256) {
devs[fd][0] = '\0';
}
}
return(real_close(fd));
}
/*
* Note: the following just call the real function if root is
* true. They will be used if -nonroot is ever figured out.
*/
int chown(const char *path, uid_t owner, gid_t group) {
static int (*real_chown)(const char *, uid_t, gid_t) = NULL;
CHECKIT
if (! real_chown) {
real_chown = (int (*)(const char *, uid_t, gid_t))
dlsym(RTLD_NEXT, "chown");
}
if (root) {
return(real_chown(path, owner, group));
}
if (debug) fprintf(stderr, "CHOWN: %s %d %d\n", path, owner, group);
if (strstr(path, "/dev") == path) {
if (debug) fprintf(stderr, "CHOWN: return 0\n");
return 0;
}
return(real_chown(path, owner, group));
}
int ioperm(unsigned long from, unsigned long num, int turn_on) {
static int (*real_ioperm)(unsigned long, unsigned long, int) = NULL;
CHECKIT
if (! real_ioperm) {
real_ioperm = (int (*)(unsigned long, unsigned long, int))
dlsym(RTLD_NEXT, "ioperm");
}
if (root) {
return(real_ioperm(from, num, turn_on));
}
return 0;
}
int iopl(int level) {
static int (*real_iopl)(int) = NULL;
CHECKIT
if (! real_iopl) {
real_iopl = (int (*)(int)) dlsym(RTLD_NEXT, "iopl");
}
if (root) {
return(real_iopl(level));
}
return 0;
}
uid_t getuid(void) {
static uid_t (*real_getuid)(void) = NULL;
CHECKIT
if (! real_getuid) {
real_getuid = (uid_t (*)(void)) dlsym(RTLD_NEXT, "getuid");
}
if (root) {
return(real_getuid());
}
if (debug) fprintf(stderr, "GETUID: 0\n");
return 0;
}
uid_t geteuid(void) {
static uid_t (*real_geteuid)(void) = NULL;
CHECKIT
if (! real_geteuid) {
real_geteuid = (uid_t (*)(void)) dlsym(RTLD_NEXT, "geteuid");
}
if (root) {
return(real_geteuid());
}
if (debug) fprintf(stderr, "GETEUID: 0\n");
return 0;
}
uid_t getuid32(void) {
static uid_t (*real_getuid32)(void) = NULL;
CHECKIT
if (! real_getuid32) {
real_getuid32 = (uid_t (*)(void)) dlsym(RTLD_NEXT, "getuid32");
}
if (root) {
return(real_getuid32());
}
if (debug) fprintf(stderr, "GETUID32: 0\n");
return 0;
}
uid_t geteuid32(void) {
static uid_t (*real_geteuid32)(void) = NULL;
CHECKIT
if (! real_geteuid32) {
real_geteuid32 = (uid_t (*)(void)) dlsym(RTLD_NEXT, "geteuid32");
}
if (root) {
return(real_geteuid32());
}
if (debug) fprintf(stderr, "GETEUID32: 0\n");
return 0;
}
gid_t getgid(void) {
static gid_t (*real_getgid)(void) = NULL;
CHECKIT
if (! real_getgid) {
real_getgid = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getgid");
}
if (root) {
return(real_getgid());
}
if (debug) fprintf(stderr, "GETGID: 0\n");
return 0;
}
gid_t getegid(void) {
static gid_t (*real_getegid)(void) = NULL;
CHECKIT
if (! real_getegid) {
real_getegid = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getegid");
}
if (root) {
return(real_getegid());
}
if (debug) fprintf(stderr, "GETEGID: 0\n");
return 0;
}
gid_t getgid32(void) {
static gid_t (*real_getgid32)(void) = NULL;
CHECKIT
if (! real_getgid32) {
real_getgid32 = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getgid32");
}
if (root) {
return(real_getgid32());
}
if (debug) fprintf(stderr, "GETGID32: 0\n");
return 0;
}
gid_t getegid32(void) {
static gid_t (*real_getegid32)(void) = NULL;
CHECKIT
if (! real_getegid32) {
real_getegid32 = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getegid32");
}
if (root) {
return(real_getegid32());
}
if (debug) fprintf(stderr, "GETEGID32: 0\n");
return 0;
}
#code_end
}
......@@ -21,6 +21,11 @@ char *unixpw_list = NULL;
int use_stunnel = 0; /* -stunnel */
int stunnel_port = 0;
char *stunnel_pem = NULL;
int use_openssl = 0;
char *openssl_pem = NULL;
char *ssl_verify = NULL;
int ssl_initialized = 0;
int usepw = USEPW;
char *blackout_str = NULL; /* -blackout */
int blackout_ptr = 0;
char *clip_str = NULL; /* -clip */
......
......@@ -21,6 +21,11 @@ extern char *unixpw_list;
extern int use_stunnel;
extern int stunnel_port;
extern char *stunnel_pem;
extern int use_openssl;
extern char *openssl_pem;
extern char *ssl_verify;
extern int ssl_initialized;
extern int usepw;
extern char *blackout_str;
extern int blackout_ptr;
extern char *clip_str;
......
......@@ -3775,10 +3775,18 @@ char *process_remote_cmd(char *cmd, int stringonly) {
snprintf(buf, bufn, "aro=%s:%d", p, unixpw_nis);
} else if (!strcmp(p, "unixpw_list")) {
snprintf(buf, bufn, "aro=%s:%s", p, NONUL(unixpw_list));
} else if (!strcmp(p, "ssl")) {
snprintf(buf, bufn, "aro=%s:%d", p, use_openssl);
} else if (!strcmp(p, "ssl_pem")) {
snprintf(buf, bufn, "aro=%s:%s", p, NONUL(openssl_pem));
} else if (!strcmp(p, "sslverify")) {
snprintf(buf, bufn, "aro=%s:%s", p, NONUL(ssl_verify));
} else if (!strcmp(p, "stunnel")) {
snprintf(buf, bufn, "aro=%s:%d", p, use_stunnel);
} else if (!strcmp(p, "stunnel_pem")) {
snprintf(buf, bufn, "aro=%s:%s", p, NONUL(stunnel_pem));
} else if (!strcmp(p, "usepw")) {
snprintf(buf, bufn, "aro=%s:%d", p, usepw);
} else if (!strcmp(p, "using_shm")) {
snprintf(buf, bufn, "aro=%s:%d", p, !using_shm);
} else if (!strcmp(p, "logfile") || !strcmp(p, "o")) {
......
......@@ -2146,8 +2146,9 @@ static void ping_clients(int tile_cnt) {
}
if (tile_cnt) {
last_send = now;
} else if (now - last_send > 1) {
} else if (now - last_send > 2) {
/* Send small heartbeat to client */
if (0) fprintf(stderr, "ping_clients 2\n");
mark_rect_as_modified(0, 0, 1, 1, 1);
last_send = now;
}
......@@ -2638,6 +2639,8 @@ int scan_for_updates(int count_only) {
/* Work around threaded rfbProcessClientMessage() calls timeouts */
if (use_threads) {
ping_clients(tile_diffs);
} else if (use_openssl && !tile_diffs) {
ping_clients(0);
}
......
......@@ -18,11 +18,13 @@
#include "remote.h"
#include "unixpw.h"
#include "sslcmds.h"
#include "sslhelper.h"
void set_colormap(int reset);
void set_nofb_params(int restore);
void set_raw_fb_params(int restore);
void do_new_fb(int reset_mem);
void free_old_fb(char *old_main, char *old_rfb, char *old_8to24);
void check_padded_fb(void);
void install_padded_fb(char *geom);
XImage *initialize_xdisplay_fb(void);
......@@ -552,6 +554,18 @@ static void nofb_hook(rfbClientPtr cl) {
screen->displayHook = NULL;
}
void free_old_fb(char *old_main, char *old_rfb, char *old_8to24) {
if (old_main) {
free(old_main);
}
if (old_rfb && old_rfb != old_main) {
free(old_rfb);
}
if (old_8to24 && old_8to24 != old_main && old_8to24 != old_rfb) {
free(old_8to24);
}
}
void do_new_fb(int reset_mem) {
XImage *fb;
char *old_main = main_fb;
......@@ -580,15 +594,8 @@ void do_new_fb(int reset_mem) {
initialize_polling_images();
}
if (old_main) {
free(old_main);
}
if (old_rfb && old_rfb != old_main) {
free(old_rfb);
}
if (old_8to24 && old_8to24 != old_main && old_8to24 != old_rfb) {
free(old_8to24);
}
free_old_fb(old_main, old_rfb, old_8to24);
fb0 = fb;
}
......@@ -1572,7 +1579,9 @@ void initialize_screen(int *argc, char **argv, XImage *fb) {
/* n.b. samplesPerPixel (set = 1 here) seems to be unused. */
if (create_screen) {
if (use_stunnel) {
if (use_openssl) {
openssl_init();
} else if (use_stunnel) {
setup_stunnel(0, argc, argv);
}
screen = rfbGetScreen(argc, argv, width, height,
......@@ -1954,6 +1963,10 @@ void initialize_screen(int *argc, char **argv, XImage *fb) {
rfbInitServer(screen);
if (use_openssl) {
openssl_port();
}
install_passwds();
}
......
......@@ -7,6 +7,7 @@ extern void set_colormap(int reset);
extern void set_nofb_params(int restore);
extern void set_raw_fb_params(int restore);
extern void do_new_fb(int reset_mem);
extern void free_old_fb(char *old_main, char *old_rfb, char *old_8to24);
extern void check_padded_fb(void);
extern void install_padded_fb(char *geom);
extern XImage *initialize_xdisplay_fb(void);
......
......@@ -302,7 +302,7 @@ void selection_send(XEvent *ev) {
PROP_MAX);
break;
}
if (debug_sel) fprintf(stderr, "selection_send: data: '%s' dlen: %d nitems: %d ba: %d\n", data, dlen, nitems, bytes_after);
if (debug_sel) fprintf(stderr, "selection_send: data: '%s' dlen: %d nitems: %lu ba: %lu\n", data, dlen, nitems, bytes_after);
memcpy(selection_str+slen, data, dlen);
slen += dlen;
selection_str[slen] = '\0';
......
......@@ -46,6 +46,7 @@ int start_stunnel(int stunnel_port, int x11vnc_port) {
char extra[] = ":/usr/sbin:/usr/local/sbin";
char *path, *p, *exe;
char *stunnel_path = NULL;
struct stat verify_buf;
int status;
if (stunnel_pid) {
......@@ -110,13 +111,11 @@ int start_stunnel(int stunnel_port, int x11vnc_port) {
stunnel_port, x11vnc_port);
}
if (0) {
fprintf(stderr, "foreground = yes\n");
fprintf(stderr, "pid =\n");
fprintf(stderr, ";debug = 7\n");
fprintf(stderr, "[x11vnc_stunnel]\n");
fprintf(stderr, "accept = %d\n", stunnel_port);
fprintf(stderr, "connect = %d\n", x11vnc_port);
if (ssl_verify) {
if (stat(ssl_verify, &verify_buf) != 0) {
rfbLog("stunnel: %s does not exist.\n", ssl_verify);
return 0;
}
}
stunnel_pid = fork();
......@@ -137,18 +136,37 @@ int start_stunnel(int stunnel_port, int x11vnc_port) {
}
if (use_stunnel == 3) {
char sp[20], xp[20];
char sp[20], xp[20], *a = NULL;
char *st = stunnel_path;
char *pm = stunnel_pem;
char *sv = ssl_verify;
sprintf(sp, "%d", stunnel_port);
sprintf(xp, "%d", x11vnc_port);
if (stunnel_pem) {
execlp(stunnel_path, stunnel_path, "-f", "-d",
sp, "-r", xp, "-P", "none", "-p",
stunnel_pem, (char *) NULL);
if (ssl_verify) {
if(S_ISDIR(verify_buf.st_mode)) {
a = "-a";
} else {
a = "-A";
}
}
if (stunnel_pem && ssl_verify) {
execlp(st, st, "-f", "-d", sp, "-r", xp, "-P",
"none", "-p", pm, a, sv, "-v", "2",
(char *) NULL);
} else if (stunnel_pem && !ssl_verify) {
execlp(st, st, "-f", "-d", sp, "-r", xp, "-P",
"none", "-p", pm,
(char *) NULL);
} else if (!stunnel_pem && ssl_verify) {
execlp(st, st, "-f", "-d", sp, "-r", xp, "-P",
"none", a, sv, "-v", "2",
(char *) NULL);
} else {
execlp(stunnel_path, stunnel_path, "-f", "-d",
sp, "-r", xp, "-P", "none", (char *) NULL);
execlp(st, st, "-f", "-d", sp, "-r", xp, "-P",
"none", (char *) NULL);
}
exit(1);
}
......@@ -162,7 +180,15 @@ int start_stunnel(int stunnel_port, int x11vnc_port) {
if (stunnel_pem) {
fprintf(in, "cert = %s\n", stunnel_pem);
}
fprintf(in, ";debug = 7\n");
if (ssl_verify) {
if(S_ISDIR(verify_buf.st_mode)) {
fprintf(in, "CApath = %s\n", ssl_verify);
} else {
fprintf(in, "CAfile = %s\n", ssl_verify);
}
fprintf(in, "verify = 2\n");
}
fprintf(in, ";debug = 7\n\n");
fprintf(in, "[x11vnc_stunnel]\n");
fprintf(in, "accept = %d\n", stunnel_port);
fprintf(in, "connect = %d\n", x11vnc_port);
......@@ -256,6 +282,7 @@ void setup_stunnel(int rport, int *argc, char **argv) {
}
}
stunnel_port = rport;
ssl_initialized = 1;
return;
}
......
/* -- sslhelper.c -- */
#include "x11vnc.h"
#include "inet.h"
#include "cleanup.h"
#include "screen.h"
#include "scan.h"
#if LIBVNCSERVER_HAVE_FORK
#if LIBVNCSERVER_HAVE_SYS_WAIT_H && LIBVNCSERVER_HAVE_WAITPID
#define FORK_OK
#endif
#endif
int openssl_sock = -1;
pid_t openssl_last_helper_pid = 0;
#if !LIBVNCSERVER_HAVE_LIBSSL
int openssl_present(void) {return 0;}
void openssl_init(void) {
rfbLog("not compiled with libssl support.\n");
clean_up_exit(1);
}
void openssl_port(void) {}
void check_openssl(void) {}
void ssh_helper_pid(pid_t pid, int sock) {sock = pid;}
#else
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
int openssl_present(void);
void openssl_init(void);
void openssl_port(void);
void check_openssl(void);
void ssh_helper_pid(pid_t pid, int sock);
static SSL_CTX *ctx = NULL;
static RSA *rsa_512 = NULL;
static RSA *rsa_1024 = NULL;
static SSL *ssl = NULL;
static void init_prng(void);
static void sslerrexit(void);
static char *create_tmp_pem(void);
static int ssl_init(int csock, int ssock);
static void ssl_xfer(int csock, int ssock);
#ifndef FORK_OK
void openssl_init(void) {
rfbLog("openssl_init: fork is not supported. cannot create"
" ssl helper process.\n");
clean_up_exit(1);
}
int openssl_present(void) {return 0;}
#else
int openssl_present(void) {return 1;}
static void sslerrexit(void) {
unsigned long err = ERR_get_error();
char str[256];
if (err) {
ERR_error_string(err, str);
fprintf(stderr, "ssl error: %s\n", str);
}
clean_up_exit(1);
}
static char *create_tmp_pem(void) {
pid_t pid, pidw;
FILE *in, *out;
char cnf[] = "/tmp/x11vnc-cnf.XXXXXX";
char pem[] = "/tmp/x11vnc-pem.XXXXXX";
char str[4096], line[1024], *path, *p, *exe;
int found_openssl = 0, cnf_fd, pem_fd, status, db = 1;
struct stat sbuf;
char tmpl[] =
"[ req ]\n"
"prompt = no\n"
"default_bits = 1024\n"
"encrypt_key = yes\n"
"distinguished_name = req_dn\n"
"x509_extensions = cert_type\n"
"\n"
"[ req_dn ]\n"
"countryName=AU\n"
"localityName=%s\n"
"organizationalUnitName=%s-%f\n"
"commonName=x11vnc-%d\n"
"emailAddress=nobody@x11vnc.server\n"
"\n"
"[ cert_type ]\n"
"nsCertType = server\n"
;
if (no_external_cmds) {
rfbLog("create_tmp_pem: cannot run external commands.\n");
return NULL;
}
rfbLog("\n");
rfbLog("Creating a temporary, self-signed PEM certificate...\n");
rfbLog("This will NOT prevent man-in-the-middle attacks unless you\n");
rfbLog("get the certificate information to the VNC viewers ssl\n");
rfbLog("tunnel configuration. But it will prevent passive sniffing.\n");
if (! getenv("PATH")) {
return NULL;
}
path = strdup(getenv("PATH"));
/* find openssl binary: */
exe = (char *) malloc(strlen(path) + strlen("/openssl") + 1);
p = strtok(path, ":");
while (p) {
sprintf(exe, "%s/openssl", p);
if (stat(exe, &sbuf) == 0) {
if (! S_ISDIR(sbuf.st_mode)) {
found_openssl = 1;
break;
}
}
p = strtok(NULL, ":");
}
free(path);
if (! found_openssl) {
return NULL;
}
cnf_fd = mkstemp(cnf);
pem_fd = mkstemp(pem);
if (cnf_fd < 0 || pem_fd < 0) {
return NULL;
}
close(pem_fd);
/* create template file with our made up stuff: */
sprintf(str, tmpl, UT.sysname, UT.nodename, dnow(), (int) getpid());
write(cnf_fd, str, strlen(str));
close(cnf_fd);
/* make RSA key */
pid = fork();
if (pid < 0) {
return NULL;
} else if (pid == 0) {
int i;
for (i=0; i<256; i++) {
close(i);
}
execlp(exe, exe, "req", "-new", "-x509", "-nodes",
"-config", cnf, "-out", pem, "-keyout", pem, (char *)0);
exit(1);
}
pidw = waitpid(pid, &status, 0);
if (pidw != pid) {
return NULL;
}
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
;
} else {
return NULL;
}
/* make DH parameters */
pid = fork();
if (pid < 0) {
return NULL;
} else if (pid == 0) {
int i;
for (i=0; i<256; i++) {
close(i);
}
execlp(exe, exe, "dhparam", "-out", cnf, "512", (char *)0);
exit(1);
}
pidw = waitpid(pid, &status, 0);
if (pidw != pid) {
return NULL;
}
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
;
} else {
return NULL;
}
/* append result: */
in = fopen(cnf, "r");
if (in == NULL) {
return NULL;
}
out = fopen(pem, "a");
if (out == NULL) {
fclose(in);
return NULL;
}
while (fgets(line, 1024, in) != NULL) {
fprintf(out, "%s", line);
}
fclose(in);
fclose(out);
unlink(cnf);
free(exe);
if (db) {
char cmd[100];
sprintf(cmd, "openssl x509 -text -in %s", pem);
fprintf(stderr, "\n");
system(cmd);
fprintf(stderr, "\n");
}
return strdup(pem);
}
void openssl_init(void) {
int db = 0, tmp_pem = 0, do_dh = 1;
FILE *in;
double ds;
long mode;
if (! quiet) {
rfbLog("\n");
rfbLog("Initializing SSL.\n");
}
if (db) fprintf(stderr, "\nSSL_load_error_strings()\n");
SSL_load_error_strings();
if (db) fprintf(stderr, "SSL_library_init()\n");
SSL_library_init();
if (db) fprintf(stderr, "init_prng()\n");
init_prng();
ctx = SSL_CTX_new( SSLv23_server_method() );
if (ctx == NULL) {
rfbLog("openssl_init: SSL_CTX_new failed.\n");
sslerrexit();
}
ds = dnow();
rsa_512 = RSA_generate_key(512,RSA_F4,NULL,NULL);
if (rsa_512 == NULL) {
rfbLog("openssl_init: RSA_generate_key(512) failed.\n");
sslerrexit();
}
rfbLog("created 512 bit temporary RSA key: %.3fs\n", dnow() - ds);
ds = dnow();
rsa_1024 = RSA_generate_key(1024,RSA_F4,NULL,NULL);
if (rsa_1024 == NULL) {
rfbLog("openssl_init: RSA_generate_key(1024) failed.\n");
sslerrexit();
}
rfbLog("created 1024 bit temporary RSA key: %.3fs\n", dnow() - ds);
if (db) fprintf(stderr, "SSL_CTX_set_tmp_rsa()\n");
if (! SSL_CTX_set_tmp_rsa(ctx, rsa_1024)) {
rfbLog("openssl_init: SSL_CTX_set_tmp_rsa(1024) failed.\n");
sslerrexit();
}
mode = 0;
mode |= SSL_MODE_ENABLE_PARTIAL_WRITE;
mode |= SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER;
SSL_CTX_set_mode(ctx, mode);
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
SSL_CTX_set_timeout(ctx, 300);
ds = dnow();
if (! openssl_pem) {
openssl_pem = create_tmp_pem();
if (! openssl_pem) {
rfbLog("openssl_init: could not create temporary,"
" self-signed PEM.\n");
clean_up_exit(1);
}
tmp_pem = 1;
}
rfbLog("using PEM %s %.3fs\n", openssl_pem, dnow() - ds);
if (do_dh) {
DH *dh;
BIO *bio;
ds = dnow();
in = fopen(openssl_pem, "r");
if (in == NULL) {
rfbLogPerror("fopen");
clean_up_exit(1);
}
bio = BIO_new_fp(in, BIO_CLOSE|BIO_FP_TEXT);
if (! bio) {
rfbLog("openssl_init: BIO_new_fp() failed.\n");
sslerrexit();
}
dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
if (dh == NULL) {
rfbLog("openssl_init: PEM_read_bio_DHparams() failed.\n");
BIO_free(bio);
sslerrexit();
}
BIO_free(bio);
SSL_CTX_set_tmp_dh(ctx, dh);
rfbLog("loaded Diffie Hellman %d bits, %.3fs\n",
8*DH_size(dh), dnow()-ds);
DH_free(dh);
}
if (! SSL_CTX_use_certificate_chain_file(ctx, openssl_pem)) {
rfbLog("openssl_init: SSL_CTX_use_certificate_chain_file() failed.\n");
sslerrexit();
}
if(! SSL_CTX_use_RSAPrivateKey_file(ctx, openssl_pem,
SSL_FILETYPE_PEM)) {
rfbLog("openssl_init: SSL_CTX_set_tmp_rsa(1024) failed.\n");
sslerrexit();
}
if (! SSL_CTX_check_private_key(ctx)) {
rfbLog("openssl_init: SSL_CTX_set_tmp_rsa(1024) failed.\n");
sslerrexit();
}
if (tmp_pem && ! getenv("X11VNC_KEEP_TMP_PEM")) {
if (getenv("X11VNC_SHOW_TMP_PEM")) {
FILE *in = fopen(openssl_pem, "r");
if (in != NULL) {
char line[128];
fprintf(stderr, "\n");
while (fgets(line, 128, in) != NULL) {
fprintf(stderr, "%s", line);
}
fprintf(stderr, "\n");
fclose(in);
}
}
unlink(openssl_pem);
free(openssl_pem);
}
if (ssl_verify) {
struct stat sbuf;
int lvl;
if (stat(ssl_verify, &sbuf) != 0) {
rfbLog("openssl_init: -sslverify does not exists %s.\n",
ssl_verify);
rfbLogPerror("stat");
clean_up_exit(1);
}
if (! S_ISDIR(sbuf.st_mode)) {
if (! SSL_CTX_load_verify_locations(ctx, ssl_verify,
NULL)) {
rfbLog("openssl_init: SSL_CTX_load_verify_"
"locations() failed.\n");
sslerrexit();
}
} else {
if (! SSL_CTX_load_verify_locations(ctx, NULL,
ssl_verify)) {
rfbLog("openssl_init: SSL_CTX_load_verify_"
"locations() failed.\n");
sslerrexit();
}
}
lvl = SSL_VERIFY_FAIL_IF_NO_PEER_CERT|SSL_VERIFY_PEER;
SSL_CTX_set_verify(ctx, lvl, NULL);
} else {
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
}
if (db) fprintf(stderr, "\n");
}
void openssl_port(void) {
int sock, shutdown = 0;
static int port = 0;
static in_addr_t iface = 0;
int db = 0;
if (! screen) {
rfbLog("openssl_port: no screen!\n");
clean_up_exit(1);
}
if (screen->listenSock > -1 && screen->port > 0) {
port = screen->port;
shutdown = 1;
}
if (screen->listenInterface) {
iface = screen->listenInterface;
}
if (shutdown) {
if (db) fprintf(stderr, "shutting down %d/%d\n",
port, screen->listenSock);
rfbShutdownSockets(screen);
}
sock = rfbListenOnTCPPort(port, iface);
if (sock < 0) {
rfbLog("openssl_port: could not reopen port %d\n", port);
clean_up_exit(1);
}
if (db) fprintf(stderr, "listen on port/sock %d/%d\n", port, sock);
openssl_sock = sock;
ssl_initialized = 1;
}
static void lose_ram(void) {
/*
* for a forked child that will be around for a long time
* without doing exec(). we really should re-exec, but a pain
* to redo all SSL ctx.
*/
free_old_fb(main_fb, rfb_fb, cmap8to24_fb);
main_fb = NULL;
rfb_fb = NULL;
cmap8to24_fb = NULL;
if (snap_fb) {
free(snap_fb);
snap_fb = NULL;
}
if (raw_fb) {
free(raw_fb);
raw_fb = NULL;
}
free_tiles();
}
void ssh_helper_pid(pid_t pid, int sock) {
# define HPSIZE 256
static pid_t helpers[HPSIZE];
static int sockets[HPSIZE], first = 1;
int i, empty, set;
if (first) {
for (i=0; i < HPSIZE; i++) {
helpers[i] = 0;
sockets[i] = 0;
}
first = 0;
}
if (pid == 0) {
/* killall */
for (i=0; i < HPSIZE; i++) {
if (helpers[i] == 0) {
continue;
sockets[i] = -1;
}
if (kill(helpers[i], 0) == 0) {
int status;
if (sockets[i] >= 0) {
close(sockets[i]);
}
kill(helpers[i], SIGTERM);
#if LIBVNCSERVER_HAVE_SYS_WAIT_H && LIBVNCSERVER_HAVE_WAITPID
waitpid(helpers[i], &status, WNOHANG);
#endif
}
helpers[i] = 0;
sockets[i] = -1;
}
return;
}
/* add */
set = 0;
empty = -1;
for (i=0; i < HPSIZE; i++) {
if (helpers[i] == pid) {
if (sock == -1) {
helpers[i] = 0;
}
sockets[i] = sock;
set = 1;
} else if (empty == -1 && helpers[i] == 0) {
empty = i;
}
}
if (set || sock == -1) {
return;
}
if (empty >= 0) {
helpers[empty] = pid;
sockets[empty] = sock;
return;
}
for (i=0; i < HPSIZE; i++) {
if (helpers[i] == 0) {
continue;
}
if (kill(helpers[i], 0) != 0) {
helpers[i] = 0;
sockets[i] = -1;
if (empty == -1) {
empty = i;
}
}
}
if (empty >= 0) {
helpers[empty] = pid;
sockets[empty] = sock;
}
}
void accept_openssl(void) {
int sock, cport, csock, vsock;
int status, n, db = 0;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
char cookie[128], rcookie[128], *name;
rfbClientPtr client;
pid_t pid;
openssl_last_helper_pid = 0;
sock = accept(openssl_sock, (struct sockaddr *)&addr, &addrlen);
if (sock < 0) {
rfbLog("accept_openssl: accept connection failed\n");
rfbLogPerror("accept");
return;
}
if (db) fprintf(stderr, "accept_openssl: sock: %d\n", sock);
cport = find_free_port(20000, 0);
if (! cport) {
rfbLog("accept_openssl: could not find open port.\n");
close(sock);
return;
}
if (db) fprintf(stderr, "accept_openssl: cport: %d\n", cport);
csock = rfbListenOnTCPPort(cport, htonl(INADDR_LOOPBACK));
if (csock < 0) {
rfbLog("accept_openssl: could not listen on port %d.\n",
cport);
close(sock);
return;
}
if (db) fprintf(stderr, "accept_openssl: csock: %d\n", csock);
fflush(stderr);
sprintf(cookie, "%f/%f", dnow(), x11vnc_start);
name = get_remote_host(sock);
if (name) {
rfbLog("SSL: spawning helper process to handle: %s\n", name);
free(name);
}
pid = fork();
if (pid < 0) {
rfbLog("accept_openssl: could not fork.\n");
rfbLogPerror("fork");
close(sock);
close(csock);
return;
} else if (pid == 0) {
int i, vncsock, sslsock = sock;
signal(SIGHUP, SIG_DFL);
signal(SIGINT, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
for (i=0; i<256; i++) {
if (i != sslsock && i != 2) {
close(i);
}
}
lose_ram();
vncsock = rfbConnectToTcpAddr("127.0.0.1", cport);
if (vncsock < 0) {
close(vncsock);
exit(1);
}
if (! ssl_init(vncsock, sslsock)) {
close(vncsock);
exit(1);
}
write(vncsock, cookie, strlen(cookie));
ssl_xfer(vncsock, sslsock);
exit(0);
}
close(sock);
vsock = accept(csock, (struct sockaddr *)&addr, &addrlen);
close(csock);
if (vsock < 0) {
rfbLog("accept_openssl: connection from ssl_helper failed.\n");
rfbLogPerror("accept");
kill(pid, SIGTERM);
waitpid(pid, &status, WNOHANG);
return;
}
if (db) fprintf(stderr, "accept_openssl: vsock: %d\n", vsock);
n = read(vsock, rcookie, strlen(cookie));
if (n != (int) strlen(cookie) || strncmp(cookie, rcookie, n)) {
rfbLog("accept_openssl: cookie from ssl_helper failed. %d\n", n);
if (errno != 0) {
rfbLogPerror("read");
}
if (db) fprintf(stderr, "'%s' '%s'\n", cookie, rcookie);
close(vsock);
kill(pid, SIGTERM);
waitpid(pid, &status, WNOHANG);
return;
}
if (db) fprintf(stderr, "accept_openssl: cookie good: %s\n", cookie);
rfbLog("SSL: handshake with helper process succeeded.\n");
openssl_last_helper_pid = pid;
ssh_helper_pid(pid, vsock);
client = rfbNewClient(screen, vsock);
openssl_last_helper_pid = 0;
if (client) {
if (db) fprintf(stderr, "accept_openssl: client %p\n", (void *) client);
} else {
rfbLog("accept_openssl: rfbNewClient failed.\n");
close(vsock);
kill(pid, SIGTERM);
waitpid(pid, &status, WNOHANG);
return;
}
}
static void ssl_timeout (int sig) {
fprintf(stderr, "sig: %d, ssh_init timed out.\n", sig);
exit(1);
}
static int ssl_init(int csock, int ssock) {
unsigned char *sid = (unsigned char *) "x11vnc SID";
char *name;
int db = 0, rc, err;
if (db) fprintf(stderr, "ssl_init: %d %d\n", csock, ssock);
ssl = SSL_new(ctx);
if (ssl == NULL) {
fprintf(stderr, "SSL_new failed\n");
return 0;
}
SSL_set_session_id_context(ssl, sid, strlen((char *)sid));
if (! SSL_set_fd(ssl, ssock)) {
fprintf(stderr, "SSL_set_fd failed\n");
return 0;
}
SSL_set_accept_state(ssl);
name = get_remote_host(ssock);
while (1) {
if (db) fprintf(stderr, "calling SSL_accept...\n");
signal(SIGALRM, ssl_timeout);
alarm(20);
rc = SSL_accept(ssl);
err = SSL_get_error(ssl, rc);
alarm(0);
signal(SIGALRM, SIG_DFL);
if (db) fprintf(stderr, "SSL_accept %d/%d\n", rc, err);
if (err == SSL_ERROR_NONE) {
break;
} else if (err == SSL_ERROR_WANT_READ) {
if (db) fprintf(stderr, "got SSL_ERROR_WANT_READ\n");
rfbLog("SSL: ssh_helper: SSL_accept() failed for: %s\n",
name);
return 0;
} else if (err == SSL_ERROR_WANT_WRITE) {
if (db) fprintf(stderr, "got SSL_ERROR_WANT_WRITE\n");
rfbLog("SSL: ssh_helper: SSL_accept() failed for: %s\n",
name);
return 0;
} else if (err == SSL_ERROR_SYSCALL) {
if (db) fprintf(stderr, "got SSL_ERROR_SYSCALL\n");
rfbLog("SSL: ssh_helper: SSL_accept() failed for: %s\n",
name);
return 0;
} else if (rc < 0) {
rfbLog("SSL: ssh_helper: SSL_accept() fatal: %d\n",
rc);
return 0;
}
}
rfbLog("SSL: ssh_helper: SSL_accept() succeeded for: %s\n", name);
free(name);
return 1;
}
static void ssl_xfer_debug(int csock, int ssock) {
char buf[2048];
int sz = 2048, n, m, status;
pid_t pid = fork();
int db = 1;
/* this is for testing, no SSL just socket redir */
if (pid < 0) {
exit(1);
}
if (pid) {
if (db) fprintf(stderr, "ssl_xfer start: %d -> %d\n", csock, ssock);
while (1) {
n = read(csock, buf, sz);
if (n == 0 || (n < 0 && errno != EINTR) ) {
break;
} else if (n > 0) {
m = write(ssock, buf, n);
if (m != n) {
if (db) fprintf(stderr, "ssl_xfer bad write: %d -> %d | %d/%d\n", csock, ssock, m, n);
break;
}
}
}
kill(pid, SIGTERM);
waitpid(pid, &status, WNOHANG);
if (db) fprintf(stderr, "ssl_xfer done: %d -> %d\n", csock, ssock);
} else {
if (db) fprintf(stderr, "ssl_xfer start: %d <- %d\n", csock, ssock);
while (1) {
n = read(ssock, buf, sz);
if (n == 0 || (n < 0 && errno != EINTR) ) {
break;
} else if (n > 0) {
m = write(csock, buf, n);
if (m != n) {
if (db) fprintf(stderr, "ssl_xfer bad write: %d <- %d | %d/%d\n", csock, ssock, m, n);
break;
}
}
}
if (db) fprintf(stderr, "ssl_xfer done: %d <- %d\n", csock, ssock);
}
close(csock);
close(ssock);
exit(0);
}
#define BSIZE 16384
static void ssl_xfer(int csock, int ssock) {
int db = 0, check_pending, fdmax, nfd, n, err;
char cbuf[BSIZE], sbuf[BSIZE];
int cptr, sptr, c_rd, c_wr, s_rd, s_wr;
fd_set rd, wr;
struct timeval tv;
if (db) {
ssl_xfer_debug(csock, ssock);
return;
}
/*
* csock: clear text socket with libvncserver. "C"
* ssock: ssl data socket with remote vnc viewer. "S"
*
* cbuf[] is data from csock that we have read but not passed on to ssl
* sbuf[] is data from ssl that we have read but not passed on to csock
*/
if (csock > ssock) {
fdmax = csock;
} else {
fdmax = ssock;
}
c_rd = 1; /* clear text (libvncserver) socket open for reading */
c_wr = 1; /* clear text (libvncserver) socket open for writing */
s_rd = 1; /* ssl data (remote client) socket open for reading */
s_wr = 1; /* ssl data (remote client) socket open for writing */
cptr = 0; /* offsets into BSIZE buffers */
sptr = 0;
while (1) {
int c_to_s, s_to_c;
if ( s_wr && (c_rd || cptr > 0) ) {
/*
* S is writable and
* C is readable or some cbuf data remaining
*/
c_to_s = 1;
} else {
c_to_s = 0;
}
if ( c_wr && (s_rd || sptr > 0) ) {
/*
* C is writable and
* S is readable or some sbuf data remaining
*/
s_to_c = 1;
} else {
s_to_c = 0;
}
if (! c_to_s && ! s_to_c) {
/*
* nothing can be sent either direction.
* break out of the loop to finish all work.
*/
break;
}
/* set up the fd sets for the two sockets for read & write: */
FD_ZERO(&rd);
if (c_rd && cptr < BSIZE) {
/* we could read more from C since cbuf is not full */
FD_SET(csock, &rd);
}
if (s_rd) {
/*
* we could read more from S since sbuf not full,
* OR ssl is waiting for more BIO to be able to
* read and we have some C data still buffered.
*/
if (sptr < BSIZE || (cptr > 0 && SSL_want_read(ssl))) {
FD_SET(ssock, &rd);
}
}
FD_ZERO(&wr);
if (c_wr && sptr > 0) {
/* we could write more to C since sbuf is not empty */
FD_SET(csock, &wr);
}
if (s_wr) {
/*
* we could write more to S since cbuf not empty,
* OR ssl is waiting for more BIO to be able
* write and we haven't filled up sbuf yet.
*/
if (cptr > 0 || (sptr < BSIZE && SSL_want_write(ssl))) {
FD_SET(ssock, &wr);
}
}
tv.tv_sec = 20;
tv.tv_usec = 0;
/* do the select, repeat if interrupted */
do {
nfd = select(fdmax+1, &rd, &wr, NULL, &tv);
} while (nfd < 0 && errno == EINTR);
if (nfd < 0) {
fprintf(stderr, "select error: %d\n", nfd);
perror("select");
/* connection finished */
return;
}
if (nfd == 0) {
fprintf(stderr, "timeout\n");
/* connection finished */
return;
}
/* used to see if SSL_pending() should be checked: */
check_pending = 0;
if (c_wr && FD_ISSET(csock, &wr)) {
/* try to write some of our sbuf to C: */
n = write(csock, sbuf, sptr);
if (n < 0) {
if (errno != EINTR) {
/* connection finished */
return;
}
/* proceed */
} else if (n == 0) {
/* connection finished XXX double check */
return;
} else {
/* shift over the data in sbuf by n */
memmove(sbuf, sbuf + n, sptr - n);
if (sptr == BSIZE) {
check_pending = 1;
}
sptr -= n;
if (! s_rd && sptr == 0) {
/* finished sending last of sbuf */
shutdown(csock, SHUT_WR);
c_wr = 0;
}
}
}
if (s_wr) {
if ((cptr > 0 && FD_ISSET(ssock, &wr)) ||
(SSL_want_read(ssl) && FD_ISSET(ssock, &rd))) {
/* try to write some of our cbuf to S: */
n = SSL_write(ssl, cbuf, cptr);
err = SSL_get_error(ssl, n);
if (err == SSL_ERROR_NONE) {
/* shift over the data in cbuf by n */
memmove(cbuf, cbuf + n, cptr - n);
cptr -= n;
if (! c_rd && cptr == 0 && s_wr) {
/* finished sending last cbuf */
SSL_shutdown(ssl);
s_wr = 0;
}
} else if (err == SSL_ERROR_WANT_WRITE
|| err == SSL_ERROR_WANT_READ
|| err == SSL_ERROR_WANT_X509_LOOKUP) {
; /* proceed */
} else if (err == SSL_ERROR_SYSCALL) {
if (n < 0 && errno != EINTR) {
/* connection finished */
return;
}
/* proceed */
} else if (err == SSL_ERROR_ZERO_RETURN) {
/* S finished */
s_rd = 0;
s_wr = 0;
} else if (err == SSL_ERROR_SSL) {
/* connection finished */
return;
}
}
}
if (c_rd && FD_ISSET(csock, &rd)) {
/* try to read some data from C into our cbuf */
n = read(csock, cbuf + cptr, BSIZE - cptr);
if (n < 0) {
if (errno != EINTR) {
/* connection finished */
return;
}
/* proceed */
} else if (n == 0) {
/* C is EOF */
c_rd = 0;
if (cptr == 0 && s_wr) {
/* and no more in cbuf to send */
SSL_shutdown(ssl);
s_wr = 0;
}
} else {
/* good */
cptr += n;
}
}
if (s_rd) {
if ((sptr < BSIZE && FD_ISSET(ssock, &rd)) ||
(SSL_want_write(ssl) && FD_ISSET(ssock, &wr)) ||
(check_pending && SSL_pending(ssl))) {
/* try to read some data from S into our sbuf */
n = SSL_read(ssl, sbuf + sptr, BSIZE - sptr);
err = SSL_get_error(ssl, n);
if (err == SSL_ERROR_NONE) {
/* good */
sptr += n;
} else if (err == SSL_ERROR_WANT_WRITE
|| err == SSL_ERROR_WANT_READ
|| err == SSL_ERROR_WANT_X509_LOOKUP) {
; /* proceed */
} else if (err == SSL_ERROR_SYSCALL) {
if (n < 0) {
if(errno != EINTR) {
/* connection finished */
return;
}
/* proceed */
} else {
/* S finished */
s_rd = 0;
s_wr = 0;
}
} else if (err == SSL_ERROR_ZERO_RETURN) {
/* S is EOF */
s_rd = 0;
if (cptr == 0 && s_wr) {
/* and no more in cbuf to send */
SSL_shutdown(ssl);
s_wr = 0;
}
if (sptr == 0 && c_wr) {
/* and no more in sbuf to send */
shutdown(csock, SHUT_WR);
c_wr = 0;
}
} else if (err == SSL_ERROR_SSL) {
/* connection finished */
return;
}
}
}
}
}
void check_openssl(void) {
fd_set fds;
struct timeval tv;
int nfds;
if (0) fprintf(stderr, "check_openssl()\n");
if (! use_openssl || openssl_sock < 0) {
return;
}
FD_ZERO(&fds);
FD_SET(openssl_sock, &fds);
tv.tv_sec = 0;
tv.tv_usec = 0;
nfds = select(openssl_sock+1, &fds, NULL, NULL, &tv);
if (nfds <= 0) {
return;
}
accept_openssl();
}
#define MSZ 4096
static void init_prng(void) {
int db = 0, bytes;
char file[MSZ];
RAND_file_name(file, MSZ);
rfbLog("RAND_file_name: %s\n", file);
bytes = RAND_load_file(file, -1);
if (db) fprintf(stderr, "bytes read: %d\n", bytes);
bytes += RAND_load_file("/dev/urandom", 64);
if (db) fprintf(stderr, "bytes read: %d\n", bytes);
if (bytes > 0) {
if (! quiet) {
rfbLog("initialized PRNG with %d random bytes.\n",
bytes);
}
return;
}
bytes += RAND_load_file("/dev/random", 8);
if (db) fprintf(stderr, "bytes read: %d\n", bytes);
if (! quiet) {
rfbLog("initialized PRNG with %d random bytes.\n", bytes);
}
}
#endif /* FORK_OK */
#endif /* LIBVNCSERVER_HAVE_LIBSSL */
#ifndef _X11VNC_SSLHELPER_H
#define _X11VNC_SSLHELPER_H
/* -- sslhelper.h -- */
extern int openssl_sock;
extern pid_t openssl_last_helper_pid;
extern int openssl_present(void);
extern void openssl_init(void);
extern void openssl_port(void);
extern void check_openssl(void);
extern void ssh_helper_pid(pid_t pid, int sock);
#endif /* _X11VNC_SSLHELPER_H */
......@@ -249,6 +249,8 @@ Debugging
=GA tail-logfile
quiet
--
=GA show-start-cmd
=DG debug_gui
=GAL Misc-Debug::
debug_xevents
debug_xdamage
......@@ -259,8 +261,6 @@ Debugging
debug_sel
dbg
=GAL LOFF
=GA show-start-cmd
=DG debug_gui
Permissions
=DRQA lock
......@@ -289,19 +289,22 @@ Permissions
unixpw_nis_list:
=0 storepasswd
=GAL LOFF
=GAL Safe::
=GAL SSL::
ssl
=F ssl_pem:
stunnel
=F stunnel_pem:
=F sslverify:
=GAL LOFF
=GAL Misc-Perms::
safer
unsafe
=RA noremote
=GAL LOFF
=GAL Misc-Perms::
=0S alwaysshared
=0S nevershared
=0S dontdisconnect
=SQA deny_all
timeout:
stunnel
=F stunnel_pem:
=GAL LOFF
Tuning
......@@ -568,6 +571,10 @@ Set the -unixpw_nis usernames list value.
set helptext(stunnel_pem) "
Set the -stunnel pem filename value.
"
set helptext(ssl_pem) "
Set the -ssl pem filename value.
"
set helptext(wireframe_mode) "
......@@ -786,6 +793,17 @@ work everywhere.
Regarding ViewOnly passwords (where a VNC client using that password
can only watch the screen, not interact with it), this is not available
with -rfbauth, but only with -passwdfile, -passwd, and -viewpasswd.
"
set helptext(SSL:) "
In this sub-menu we provide the options related to SSL encrpytion
and authentication.
There is a built-in mode (-ssl) using the OpenSSL library, and a 2nd
using the external stunnel program (-stunnel, that needs to be installed
on the system). Either may require or benefit from having PEM certificate
files specified.
"
set helptext(Misc-Perms:) "
......@@ -5584,6 +5602,8 @@ proc get_nitem {item} {
set nitem "unixpw_nis"
} elseif {$nitem == "stunnel_pem"} {
set nitem "stunnel"
} elseif {$nitem == "ssl_pem"} {
set nitem "ssl"
} elseif {$nitem == "wireframe_mode"} {
set nitem "wireframe"
} elseif {$nitem == "solid_color"} {
......
......@@ -260,6 +260,8 @@ char gui_code[] = "";
" =GA tail-logfile\n"
" quiet\n"
" --\n"
" =GA show-start-cmd\n"
" =DG debug_gui\n"
" =GAL Misc-Debug::\n"
" debug_xevents\n"
" debug_xdamage\n"
......@@ -270,8 +272,6 @@ char gui_code[] = "";
" debug_sel\n"
" dbg\n"
" =GAL LOFF\n"
" =GA show-start-cmd\n"
" =DG debug_gui\n"
"\n"
"Permissions\n"
" =DRQA lock\n"
......@@ -300,19 +300,22 @@ char gui_code[] = "";
" unixpw_nis_list:\n"
" =0 storepasswd\n"
" =GAL LOFF\n"
" =GAL Safe::\n"
" =GAL SSL::\n"
" ssl\n"
" =F ssl_pem:\n"
" stunnel\n"
" =F stunnel_pem:\n"
" =F sslverify:\n"
" =GAL LOFF\n"
" =GAL Misc-Perms::\n"
" safer\n"
" unsafe\n"
" =RA noremote\n"
" =GAL LOFF\n"
" =GAL Misc-Perms::\n"
" =0S alwaysshared\n"
" =0S nevershared\n"
" =0S dontdisconnect\n"
" =SQA deny_all\n"
" timeout:\n"
" stunnel\n"
" =F stunnel_pem:\n"
" =GAL LOFF\n"
"\n"
"Tuning\n"
......@@ -581,6 +584,10 @@ char gui_code[] = "";
"Set the -stunnel pem filename value.\n"
"\"\n"
"\n"
" set helptext(ssl_pem) \"\n"
"Set the -ssl pem filename value.\n"
"\"\n"
"\n"
" set helptext(wireframe_mode) \"\n"
"Set the -wireframe mode string value.\n"
"\"\n"
......@@ -799,6 +806,17 @@ char gui_code[] = "";
"with -rfbauth, but only with -passwdfile, -passwd, and -viewpasswd.\n"
"\"\n"
"\n"
" set helptext(SSL:) \"\n"
"In this sub-menu we provide the options related to SSL encrpytion\n"
"and authentication.\n"
"\n"
"There is a built-in mode (-ssl) using the OpenSSL library, and a 2nd\n"
"using the external stunnel program (-stunnel, that needs to be installed\n"
"on the system). Either may require or benefit from having PEM certificate\n"
"files specified.\n"
"\n"
"\"\n"
"\n"
" set helptext(Misc-Perms:) \"\n"
"In this sub-menu we provide some lesser used permission options.\n"
"\n"
......@@ -5595,6 +5613,8 @@ char gui_code[] = "";
" set nitem \"unixpw_nis\"\n"
" } elseif {$nitem == \"stunnel_pem\"} {\n"
" set nitem \"stunnel\"\n"
" } elseif {$nitem == \"ssl_pem\"} {\n"
" set nitem \"ssl\"\n"
" } elseif {$nitem == \"wireframe_mode\"} {\n"
" set nitem \"wireframe\"\n"
" } elseif {$nitem == \"solid_color\"} {\n"
......
......@@ -279,7 +279,7 @@ void try_to_be_nobody(void) {
}
static int slave_fd = -1, alarm_fired = 0;;
static int slave_fd = -1, alarm_fired = 0;
static void close_alarm (int sig) {
if (slave_fd >= 0) {
......
......@@ -2,7 +2,7 @@
.TH X11VNC "1" "March 2006" "x11vnc " "User Commands"
.SH NAME
x11vnc - allow VNC connections to real X11 displays
version: 0.8.1, lastmod: 2006-03-08
version: 0.8.1, lastmod: 2006-03-11
.SH SYNOPSIS
.B x11vnc
[OPTION]...
......@@ -519,9 +519,9 @@ are view-only during this period.
.IP
Since the detailed behavior of
.IR su (1)
can vary from
OS to OS and for local configurations, please test
the mode carefully on your systems before using it.
can vary from OS
to OS and for local configurations, please test the mode
carefully on your systems before using it in production.
E.g. try different combinations of valid/invalid
usernames and valid/invalid passwords to see if it
behaves correctly. x11vnc will be conservative and
......@@ -541,59 +541,70 @@ problems are PAM modules that prompt for extra info,
e.g. password aging modules. These logins will fail
as well even when the correct password is supplied.
.IP
*IMPORTANT*: to prevent the Unix password being sent in
*clear text* over the network, two x11vnc options are
enforced: 1) \fB-localhost\fR and 2) \fB-stunnel.\fR The former
requires the viewer connection to appear to come from
the same machine x11vnc is running on (e.g. from a ssh
\fB-L\fR port redirection). The latter requires the \fB-stunnel\fR
SSL mode be used (see the description below).
**IMPORTANT**: to prevent the Unix password being sent
in *clear text* over the network, one of two schemes
will be enforced: 1) the \fB-ssl\fR builtin SSL mode, or 2)
require both \fB-localhost\fR and \fB-stunnel\fR be enabled.
.IP
To override these restrictions you can set environment
variables before starting x11vnc:
Method 1) ensures the traffic is encrypted between
viewer and server. A PEM file will be required, see the
discussion under \fB-ssl\fR below (under some circumstances
a temporary one can be automatically generated).
.IP
Set UNIXPW_DISABLE_STUNNEL=1 to disable using \fB-stunnel.\fR
Evidently you will be using a different method to
encrypt the data between the vncviewer and x11vnc:
e.g.
.IR ssh (1)
or a VPN. Note that use of \fB-localhost\fR
with
.IR ssh (1)
is roughly the same as requiring a Unix
user login (since a Unix password or the user's public
key authentication is used by ssh on the machine where
x11vnc runs and only local connections are accepted)
Method 2) requires the viewer connection to appear
to come from the same machine x11vnc is running on
(e.g. from a ssh \fB-L\fR port redirection). And that the
\fB-stunnel\fR SSL mode be used for encryption over the
network.(see the description of \fB-stunnel\fR below).
.IP
As a convenience, if you
.IR ssh (1)
in and start x11vnc it
will check if the environment variable SSH_CONNECTION
is set and appears reasonable. If it does, then the
stunnel requirement is dropped since it is assumed
you are using ssh for the encrypted tunnelling.
Use \fB-stunnel\fR to force stunnel usage for this case.
\fB-ssl\fR or \fB-stunnel\fR requirement will be dropped since it is
assumed you are using ssh for the encrypted tunnelling.
\fB-localhost\fR is still enforced. Use \fB-ssl\fR or \fB-stunnel\fR to
force SSL usage for this case.
.IP
To override these restrictions you can set environment
variables before starting x11vnc:
.IP
Set UNIXPW_DISABLE_SSL=1 to disable requiring either
\fB-ssl\fR or \fB-stunnel.\fR Evidently you will be using a
different method to encrypt the data between the
vncviewer and x11vnc: e.g.
.IR ssh (1)
or a VPN. Note that
use of \fB-localhost\fR with
.IR ssh (1)
is roughly the same as
requiring a Unix user login (since a Unix password or
the user's public key authentication is used by sshd on
the machine where x11vnc runs and only local connections
are accepted)
.IP
Set UNIXPW_DISABLE_LOCALHOST=1 to disable the \fB-localhost\fR
requirement. One should never do this (i.e. allow the
Unix passwords to be sniffed on the network).
requirement in Method 2). One should never do this
(i.e. allow the Unix passwords to be sniffed on the
network).
.IP
Regarding reverse connections (e.g. \fB-R\fR connect:host),
the \fB-localhost\fR constraint is in effect and the reverse
if the \fB-localhost\fR constraint is in effect then reverse
connections can only be used to connect to the same
machine x11vnc is running on (default port 5500).
Please use a ssh or stunnel port redirection to the
viewer machine to tunnel the reverse connection over
an encrypted channel. Note that Unix username and
password *will* be prompted for (unlike VNC passwords
that are skipped for reverse connections).
an encrypted channel. Note that in \fB-ssl\fR mode reverse
connection are disabled.
.IP
NOTE: in \fB-inetd\fR mode the two settings are attempted
to be enforced for reverse connections. Be sure to
XXX \fB-inetd\fR + \fB-ssl\fR
In \fB-inetd\fR mode the two settings are attempted to be
enforced for reverse connections. Be sure to also
use encryption from the viewer to inetd since x11vnc
cannot guess easily if it is encrpyted. Note: you can
cannot guess easily if it is encrpyted. Tip: you can
also have your own stunnel spawn x11vnc in \fB-inetd\fR mode
(i.e. bypassing inetd). See the FAQ.
(i.e. bypassing inetd). See the FAQ for details.
.IP
The user names in the comma separated [list] can have
per-user options after a ":", e.g. "fred:opts"
......@@ -635,21 +646,101 @@ is required), but it is unlikely it will work for any
other environment. All of the \fB-unixpw\fR options and
contraints apply.
.PP
\fB-ssl\fR \fI[pem]\fR
.IP
Use the openssl library (www.openssl.org) to provide a
built-in encrypted SSL tunnel between VNC viewers and
x11vnc. This requires libssl support to be compiled
into x11vnc at build time. If x11vnc is not built
with libssl support it will exit immediately when \fB-ssl\fR
is prescribed.
.IP
[pem] is optional, use "\fB-ssl\fR \fI/path/to/mycert.pem\fR" to
specify a PEM certificate file to use to identify and
provide a key for this server.
.IP
Connecting VNC viewer SSL tunnels can authenticate
this server if they have the public key part of the
certificate (or a common certificate authority, CA,
verifies this server's cert). This is used to prevent
man-in-the-middle attacks. Otherwise, if the VNC viewer
accepts this server's key without verification, at
least the traffic is protected from passive sniffing
on the network.
.IP
If [pem] is not supplied and the
.IR openssl (1)
utility
command exists in PATH, then a temporary, self-signed
certificate will be generated for this session (this
may take 5-20 seconds on slow machines). If
.IR openssl (1)
cannot be used to generate a temporary certificate
x11vnc exits immediately.
.IP
If successful in using
.IR openssl (1)
to generate a
certificate, the public part of it will be displayed
to stdout (e.g. one could copy it to the client-side
to provide authentication of the server to VNC viewers.)
.IP
Set the env. var. X11VNC_SHOW_TMP_PEM=1 to have x11vnc
print out the entire certificate, including the PRIVATE
KEY part, to stderr. One could reuse this cert if saved
in a [pem] file. Similarly, set X11VNC_KEEP_TMP_PEM=1
to not delete the temporary PEM file: the file name
will be printed to stderr (so one could move it to a
safe place for reuse).
.IP
Reverse connections are disabled in \fB-ssl\fR
mode because the data cannot be encrypted.
Set X11VNC_SSL_ALLOW_REVERSE=1 to override this.
.IP
Your VNC viewer will also need to be able to connect
via SSL. See the discussion below under \fB-stunnel\fR and
the FAQ for how this might be achieved. E.g. on Unix it
is easy to write a shell script that starts up stunnel
and then vncviewer.
.PP
\fB-sslverify\fR \fI[path]\fR
.IP
For either of the \fB-ssl\fR or \fB-stunnel\fR modes, use [path]
to provide certificates to authenticate incoming VNC
client connections. This can be used as a method to
replace standard password authentication.
.IP
If [path] is a directory it contains the client (or CA)
certificates in separate files. If [path] is a file, it
contains multiple certificates. These correspond to the
"CApath = dir" and "CAfile = file" stunnel options.
See the
.IR stunnel (8)
manpage for details.
.IP
To create certificates for all sorts of authentications
(clients, servers, via CA, etc) see the
.IR openssl (1)
command. Of particular usefulness is the x509
subcommand of
.IR openssl (1).
.PP
\fB-stunnel\fR \fI[pem]\fR
.IP
Use the
.IR stunnel (1)
.IR stunnel (8)
(www.stunnel.org) to provide
an encrypted SSL tunnel between viewers and x11vnc.
This requires stunnel to be installed on the system and
available via PATH (n.b. stunnel is often installed in
sbin directories). Version 4.x of stunnel is assumed;
see \fB-stunnel3\fR below.
sbin directories). Version 4.x of stunnel is assumed
(but see \fB-stunnel3\fR below.)
.IP
[pem] is optional, use "\fB-stunnel\fR \fI/path/to/stunnel.pem\fR"
to specify a PEM certificate file to pass to stunnel.
Whether one is needed or not depends on your stunnel
configuration.
configuration. stunnel often generates one at install
time.
.IP
stunnel is started up as a child process of x11vnc and
any SSL connections stunnel receives are decrypted and
......@@ -661,14 +752,15 @@ The \fB-localhost\fR option is enforced by default to
avoid people routing around the SSL channel. Set
STUNNEL_DISABLE_LOCALHOST=1 to disable the requirement.
.IP
Your VNC viewer will need to be able to connect via SSL.
Unfortunately not too many do this. UltraVNC seems to
have a SSL plugin. It is not too difficult to set up
an stunnel or other SSL tunnel on the viewer side.
Your VNC viewer will also need to be able to connect
via SSL. Unfortunately not too many do this. UltraVNC
seems to have an encryption plugin. It is not too
difficult to set up an stunnel or other SSL tunnel on
the viewer side.
.IP
A simple example on Unix using stunnel 3.x is:
.IP
% stunnel \fB-c\fR \fB-d\fR localhost:5901 \fB-r\fR remote:5900
% stunnel \fB-c\fR \fB-d\fR localhost:5901 \fB-r\fR remotehost:5900
% vncviewer localhost:1
.IP
For Windows, stunnel has been ported to it and there
......@@ -2799,22 +2891,24 @@ debug_xevents debug_xdamage nodebug_xdamage
debug_xdamage debug_wireframe nodebug_wireframe
debug_wireframe debug_scroll nodebug_scroll debug_scroll
debug_tiles dbt nodebug_tiles nodbt debug_tiles
debug_grabs nodebug_grabs dbg nodbg noremote
debug_grabs nodebug_grabs debug_sel nodebug_sel dbg
nodbg noremote
.IP
aro= noop display vncdisplay desktopname guess_desktop
http_url auth xauth users rootshift clipshift
scale_str scaled_x scaled_y scale_numer scale_denom
scale_fac scaling_blend scaling_nomult4 scaling_pad
scaling_interpolate inetd privremote unsafe safer
nocmds passwdfile unixpw unixpw_nis unixpw_list stunnel
stunnel_pem using_shm logfile o flag 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 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
nocmds passwdfile unixpw unixpw_nis unixpw_list ssl
ssl_pem sslverify stunnel stunnel_pem usepw using_shm
logfile o flag 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 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
......
......@@ -140,6 +140,7 @@
#include "unixpw.h"
#include "inet.h"
#include "sslcmds.h"
#include "sslhelper.h"
#include "selection.h"
/*
......@@ -471,6 +472,7 @@ if (debug_scroll) fprintf(stderr, "watch_loop: LOOP-BACK: %d\n", ret);
check_connect_inputs();
check_gui_inputs();
check_stunnel();
check_openssl();
record_last_fb_update();
check_padded_fb();
check_fixscreen();
......@@ -1353,7 +1355,8 @@ static void store_homedir_passwd(void) {
#define SHOW_NO_PASSWORD_WARNING \
(!got_passwd && !got_rfbauth && (!got_passwdfile || !passwd_list) \
&& !query_cmd && !remote_cmd && !unixpw && !got_gui_pw)
&& !query_cmd && !remote_cmd && !unixpw && !got_gui_pw \
&& ! ssl_verify)
int main(int argc, char* argv[]) {
......@@ -1367,12 +1370,10 @@ int main(int argc, char* argv[]) {
int got_gui_pw = 0;
int pw_loc = -1, got_passwd = 0, got_rfbauth = 0, nopw = NOPW;
int got_viewpasswd = 0, got_localhost = 0, got_passwdfile = 0;
int got_stunnel = 0;
int vpw_loc = -1;
int dt = 0, bg = 0;
int got_rfbwait = 0;
int got_httpdir = 0, try_http = 0;
int usepw = 0;
/* used to pass args we do not know about to rfbGetScreen(): */
int argc_vnc = 1; char *argv_vnc[128];
......@@ -1583,12 +1584,23 @@ int main(int argc, char* argv[]) {
}
if (strstr(arg, "_unsafe")) {
/* hidden option for testing. */
set_env("UNIXPW_DISABLE_STUNNEL", "1");
set_env("UNIXPW_DISABLE_SSL", "1");
set_env("UNIXPW_DISABLE_LOCALHOST", "1");
}
} else if (!strcmp(arg, "-ssl")) {
use_openssl = 1;
if (i < argc-1) {
char *s = argv[i+1];
if (s[0] != '-') {
openssl_pem = strdup(s);
i++;
}
}
} else if (!strcmp(arg, "-sslverify")) {
CHECK_ARGC
ssl_verify = strdup(argv[++i]);
} else if (!strcmp(arg, "-stunnel")) {
use_stunnel = 1;
got_stunnel = 1;
if (i < argc-1) {
char *s = argv[i+1];
if (s[0] != '-') {
......@@ -1598,7 +1610,6 @@ int main(int argc, char* argv[]) {
}
} else if (!strcmp(arg, "-stunnel3")) {
use_stunnel = 3;
got_stunnel = 1;
if (i < argc-1) {
char *s = argv[i+1];
if (s[0] != '-') {
......@@ -2301,6 +2312,16 @@ int main(int argc, char* argv[]) {
exit(1);
}
if (ssl_verify) {
struct stat sbuf;
if (stat(ssl_verify, &sbuf) != 0) {
rfbLog("x11vnc: -sslverify %s does not exist\n",
ssl_verify);
rfbLogPerror("stat");
exit(1);
}
}
/*
* If -passwd was used, clear it out of argv. This does not
* work on all UNIX, have to use execvp() in general...
......@@ -2382,39 +2403,39 @@ int main(int argc, char* argv[]) {
launch_gui = 0;
}
if (! inetd) {
if (unixpw) {
if (! got_localhost && ! getenv("UNIXPW_DISABLE_LOCALHOST")) {
if (! quiet) {
rfbLog("Setting -localhost in -unixpw mode.\n");
}
allow_list = strdup("127.0.0.1");
got_localhost = 1;
}
if (! got_stunnel) {
if (! getenv("UNIXPW_DISABLE_STUNNEL") &&
! have_ssh_env()) {
if (! quiet) {
rfbLog("Setting -stunnel in -unixpw "
"mode.\n");
}
use_stunnel = 1;
} else if (! getenv("UNIXPW_DISABLE_STUNNEL")) {
if (! inetd && unixpw) {
if (! use_stunnel && ! use_openssl) {
if (have_ssh_env()) {
char *s = getenv("SSH_CONNECTION");
if (! s) s = getenv("SSH_CLIENT");
if (! s) s = "SSH_CONNECTION";
fprintf(stderr, "\n");
rfbLog("Skipping -stunnel contraint in -unixpw mode,\n");
rfbLog("assuming your SSH encryption is: %s\n", s);
rfbLog("Skipping -ssl/-stunnel contraint in"
" -unixpw\n");
rfbLog("mode, assuming your SSH encryption"
" is: %s\n", s);
fprintf(stderr, "\n");
if (! nopw) {
usleep(2000*1000);
}
} else if (getenv("UNIXPW_DISABLE_SSL")) {
rfbLog("Skipping -ssl/-stunnel requirement"
" due to\n");
rfbLog("UNIXPW_DISABLE_SSL setting.\n");
} else {
if (openssl_present()) {
rfbLog("set -ssl in -unixpw mode.\n");
use_openssl = 1;
} else {
rfbLog("set -stunnel in -unixpw mode.\n");
use_stunnel = 1;
}
}
} else if (use_stunnel) {
if (! got_localhost && ! getenv("STUNNEL_DISABLE_LOCALHOST")) {
}
}
if (use_stunnel && ! got_localhost) {
if (! getenv("STUNNEL_DISABLE_LOCALHOST") &&
! getenv("UNIXPW_DISABLE_LOCALHOST")) {
if (! quiet) {
rfbLog("Setting -localhost in -stunnel mode.\n");
}
......@@ -2422,6 +2443,9 @@ int main(int argc, char* argv[]) {
got_localhost = 1;
}
}
if (ssl_verify && ! use_stunnel && ! use_openssl) {
rfbLog("-sslverify must be used with -ssl or -stunnel\n");
exit(1);
}
/* fixup settings that do not make sense */
......@@ -3041,7 +3065,7 @@ int main(int argc, char* argv[]) {
initialize_allowed_input();
if (! inetd) {
if (! inetd && ! use_openssl) {
if (! screen->port || screen->listenSock < 0) {
rfbLogEnable(1);
rfbLog("Error: could not obtain listening port.\n");
......
......@@ -56,6 +56,7 @@
* -DHARDWIRE_PASSWD=... hardwired passwords, quoting necessary.
* -DHARDWIRE_VIEWPASSWD=...
* -DNOPW=1 make -nopw the default (skip warning)
* -DUSEPW=1 make -usepw the default
* -DPASSWD_REQUIRED=1 exit unless a password is supplied.
* -DPASSWD_UNLESS_NOPW=1 exit unless a password is supplied and no -nopw.
*
......@@ -73,6 +74,9 @@
* -DSMALL_FOOTPRINT=1 for smaller binary size (no help, no gui, etc)
* use 2 or 3 for even smaller footprint.
* -DNOGUI do not include the gui tkx11vnc.
* -DSKIP_HELP=1 smaller.
* -DSKIP_XKB=1 a little smaller.
* -DSKIP_8to24=1 a little smaller.
* -DPOLL_8TO24_DELAY=N
* -DDEBUG_XEVENTS=1 enable printout for X events.
*
......@@ -93,6 +97,10 @@
#define NOPW 0
#endif
#ifndef USEPW
#define USEPW 0
#endif
#ifndef PASSWD_REQUIRED
#define PASSWD_REQUIRED 0
#endif
......@@ -116,13 +124,23 @@
#define SMALL_FOOTPRINT 0
#endif
#ifndef SKIP_XKB
#define SKIP_XKB 0
#endif
#ifndef SKIP_8TO24
#define SKIP_8TO24 0
#endif
#ifndef SKIP_HELP
#define SKIP_HELP 0
#endif
#if SMALL_FOOTPRINT
#undef NOGUI
#define NOGUI
#undef SKIP_HELP
#define SKIP_HELP 0
#endif
#define SKIP_XKB 0
#define SKIP_8TO24 0
#if (SMALL_FOOTPRINT > 1)
#undef SKIP_XKB
#undef SKIP_8TO24
......@@ -447,6 +465,8 @@ typedef struct _ClientData {
int login_viewonly;
time_t login_time;
pid_t ssh_helper_pid;
int had_cursor_shape_updates;
int had_cursor_pos_updates;
......
......@@ -15,7 +15,7 @@ int xtrap_base_event_type = 0;
int xdamage_base_event_type = 0;
/* date +'lastmod: %Y-%m-%d' */
char lastmod[] = "0.8.1 lastmod: 2006-03-08";
char lastmod[] = "0.8.1 lastmod: 2006-03-11";
/* X display info */
......
......@@ -818,7 +818,7 @@ void check_xevents(void) {
/* check for our PRIMARY request notification: */
if (watch_primary || watch_clipboard) {
int doprimary = 1, doclipboard = 2, which, own;
int doprimary = 1, doclipboard = 2, which, own = 0;
double delay = 1.0;
Atom atom;
char *req;
......
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