|  | /* | 
|  | Copyright (C) 2002-2010 Karl J. Runge <[email protected]> | 
|  | All rights reserved. | 
|  |  | 
|  | This file is part of x11vnc. | 
|  |  | 
|  | x11vnc is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 2 of the License, or (at | 
|  | your option) any later version. | 
|  |  | 
|  | x11vnc is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with x11vnc; if not, write to the Free Software | 
|  | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA | 
|  | or see <http://www.gnu.org/licenses/>. | 
|  |  | 
|  | In addition, as a special exception, Karl J. Runge | 
|  | gives permission to link the code of its release of x11vnc with the | 
|  | OpenSSL project's "OpenSSL" library (or with modified versions of it | 
|  | that use the same license as the "OpenSSL" library), and distribute | 
|  | the linked executables.  You must obey the GNU General Public License | 
|  | in all respects for all of the code used other than "OpenSSL".  If you | 
|  | modify this file, you may extend this exception to your version of the | 
|  | file, but you are not obligated to do so.  If you do not wish to do | 
|  | so, delete this exception statement from your version. | 
|  | */ | 
|  |  | 
|  | #ifndef _X11VNC_ENC_H | 
|  | #define _X11VNC_ENC_H | 
|  |  | 
|  | /* -- enc.h -- */ | 
|  |  | 
|  | #if 0 | 
|  | :r /home/runge/uvnc/ultraSC/rc4/ultravnc_dsm_helper.c | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | * ultravnc_dsm_helper.c unix/openssl UltraVNC encryption encoder/decoder. | 
|  | *                       (also a generic symmetric encryption tunnel) | 
|  | *                       (also a generic TCP relay and supports IPv6) | 
|  | * | 
|  | * compile via: | 
|  |  | 
|  | cc       -O -o ultravnc_dsm_helper ultravnc_dsm_helper.c -lssl -lcrypto | 
|  | cc -DDBG -O -o ultravnc_dsm_helper ultravnc_dsm_helper.c -lssl -lcrypto | 
|  |  | 
|  | * | 
|  | * See usage below for how to run it. | 
|  | * | 
|  | * Note: since the UltraVNC DSM plugin implementation changes the RFB | 
|  | * (aka VNC) protocol (extra data is sent), you will *ALSO* need to modify | 
|  | * your VNC viewer or server to discard (or insert) this extra data. | 
|  | * | 
|  | * This tool knows nothing about the RFB protocol: it simply | 
|  | * encrypts/decrypts a stream using a symmetric cipher, arc4 and aesv2, | 
|  | * (others have been added, see usage).  It could be used as a general | 
|  | * encrypted tunnel: | 
|  | * | 
|  | *   any-client <=> ultravnc_dsm_helper <--network--> ultravnc_dsm_helper(reverse mode) <=> any-server | 
|  | * | 
|  | * e.g. to connect a non-ultra-dsm-vnc viewer to a non-ultra-dsm-vnc server | 
|  | * without using SSH or SSL. | 
|  | * | 
|  | * It can also be used as a general TCP relay (no encryption.) | 
|  | * | 
|  | * It supports IPv6 and so can also be used as a IPv6 gateway. | 
|  | * | 
|  | * ----------------------------------------------------------------------- | 
|  | * Copyright (C) 2008-2010 Karl J. Runge <[email protected]> | 
|  | * All rights reserved. | 
|  | * | 
|  | *  This is free software; you can redistribute it and/or modify | 
|  | *  it under the terms of the GNU General Public License as published by | 
|  | *  the Free Software Foundation; version 2 of the License, or (at | 
|  | *  your option) any later version. | 
|  | * | 
|  | *  This software is distributed in the hope that it will be useful, | 
|  | *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | *  GNU General Public License for more details. | 
|  | * | 
|  | *  You should have received a copy of the GNU General Public License | 
|  | *  along with this software; if not, write to the Free Software | 
|  | *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, | 
|  | *  USA  or see <http://www.gnu.org/licenses/>. | 
|  | * | 
|  | *  In addition, as a special exception, Karl J. Runge gives permission | 
|  | *  to link the code of its release of ultravnc_dsm_helper with the | 
|  | *  OpenSSL project's "OpenSSL" library (or with modified versions of it | 
|  | *  that use the same license as the "OpenSSL" library), and distribute | 
|  | *  the linked executables.  You must obey the GNU General Public License | 
|  | *  in all respects for all of the code used other than "OpenSSL".  If you | 
|  | *  modify this file, you may extend this exception to your version of the | 
|  | *  file, but you are not obligated to do so.  If you do not wish to do | 
|  | *  so, delete this exception statement from your version. | 
|  | * ----------------------------------------------------------------------- | 
|  | */ | 
|  |  | 
|  | static char *usage = | 
|  | "\n" | 
|  | "ultravnc_dsm_helper: a symmetric encryption tunnel. version 0.2\n" | 
|  | "\n" | 
|  | "       Created to enable encrypted VNC connections to UltraVNC, it can act as\n" | 
|  | "       a general encrypted tunnel between any two applications.  It can also\n" | 
|  | "       be used as a general TCP relay (i.e. no encryption) or an IPv6 gateway.\n" | 
|  | "\n" | 
|  | "Usage: ultravnc_dsm_helper cipher keyfile listenport remotehost:port\n" | 
|  | "       ultravnc_dsm_helper relay listenport remotehost:port\n" | 
|  | "       ultravnc_dsm_helper showcert remotehost:port\n" | 
|  | "\n" | 
|  | "e.g.:  ultravnc_dsm_helper arc4 ./arc4.key 5901 snoopy.net:5900\n" | 
|  | "\n" | 
|  | "       IPv6 is supported: both IPv4 and IPv6 are attempted to listen on (port\n" | 
|  | "               'listenport'.)  For connections to remotehost, if IPv4 fails\n" | 
|  | "               then IPv6 is tried.  Set the env. var ULTRAVNC_DSM_HELPER_NOIPV6\n" | 
|  | "               to completely disable the use of IPv6.\n" | 
|  | "\n" | 
|  | "\n" | 
|  | "       cipher: specify 'msrc4', 'msrc4_sc', 'arc4', 'aesv2', 'aes-cfb',\n" | 
|  | "               'aes256', 'blowfish', '3des', 'securevnc'.\n" | 
|  | "\n" | 
|  | "               Also 'none', 'relay', or 'showcert'.  See below for details.\n" | 
|  | "\n" | 
|  | "         'msrc4_sc' enables a workaround for UVNC SC -plugin use.\n" | 
|  | "            (it might not be required in SC circa 2009 and later; try 'msrc4'.)\n" | 
|  | "\n" | 
|  | "         use 'securevnc' for SecureVNCPlugin (RSA key exchange).  'keyfile' is\n" | 
|  | "           used as a server RSA keystore in this mode.  If 'keyfile' does not\n" | 
|  | "           exist the user is prompted whether to save the key or not (a MD5\n" | 
|  | "           hash of it is shown)  If 'keyfile' already exists the server key\n" | 
|  | "           must match its contents or the connection is dropped.\n" | 
|  | "\n" | 
|  | "           HOWEVER, if 'keyfile' ends in the string 'ClientAuth.pkey', then the\n" | 
|  | "           normal SecureVNCPlugin client key authentication is performed.\n" | 
|  | "           If you want to do both have 'keyfile' end with 'ClientAuth.pkey.rsa'\n" | 
|  | "           that file will be used for the RSA keystore, and the '.rsa' will be\n" | 
|  | "           trimmed off and the remaining name used as the Client Auth file.\n" | 
|  | "\n" | 
|  | "         use '.' to have it try to guess the cipher from the keyfile name,\n" | 
|  | "           e.g. 'arc4.key' implies arc4, 'rc4.key' implies msrc4, etc.\n" | 
|  | "\n" | 
|  | "         use 'rev:arc4', etc. to reverse the roles of encrypter and decrypter.\n" | 
|  | "           (i.e. if you want to use it for a vnc server, not vnc viewer)\n" | 
|  | "\n" | 
|  | "         use 'noultra:...' to skip steps involving salt and IV to try to be\n" | 
|  | "           compatible with UltraVNC DSM, i.e. assume a normal symmetric cipher\n" | 
|  | "           at the other end.\n" | 
|  | "\n" | 
|  | "         use 'noultra:rev:...' if both are to be supplied.\n" | 
|  | "\n" | 
|  | "\n" | 
|  | "       keyfile: file holding the key (16 bytes for arc4 and aesv2, 87 for msrc4)\n" | 
|  | "           E.g. dd if=/dev/random of=./my.key bs=16 count=1\n" | 
|  | "           keyfile can also be pw=<string> to use \"string\" for the key.\n" | 
|  | "           Or for 'securevnc' the RSA keystore and/or ClientAuth file.\n" | 
|  | "\n" | 
|  | "\n" | 
|  | "       listenport: port to listen for incoming connection on. (use 0 to connect\n" | 
|  | "                   to stdio, use a negative value to force localhost listening)\n" | 
|  | "\n" | 
|  | "\n" | 
|  | "       remotehost:port: host and port to connect to. (e.g. ultravnc server)\n" | 
|  | "\n" | 
|  | "\n" | 
|  | "       Also: cipher may be cipher@n,m where n is the salt size and m is the\n" | 
|  | "       initialization vector size. E.g. aesv2@8,16  Use n=-1 to disable salt\n" | 
|  | "       and the MD5 hash (i.e. insert the keydata directly into the cipher.)\n" | 
|  | "\n" | 
|  | "       Use cipher@md+n,m to change the message digest. E.g. arc4@sha+8,16\n" | 
|  | "       Supported: 'md5', 'sha', 'sha1', 'ripemd160'.\n" | 
|  | "\n" | 
|  | "\n" | 
|  | "       TCP Relay mode: to connect without any encryption use a cipher type of\n" | 
|  | "       either 'relay' or 'none' (both are the equivalent):\n" | 
|  | "\n" | 
|  | "         ultravnc_dsm_helper relay listenport remotehost:port\n" | 
|  | "         ultravnc_dsm_helper none  listenport remotehost:port\n" | 
|  | "\n" | 
|  | "       where 'relay' or 'none' is a literal string.\n" | 
|  | "       Note that for this mode no keyfile is suppled.\n" | 
|  | "       Note that this mode can act as an IPv4 to IPv6 gateway.\n" | 
|  | "\n" | 
|  | "         ultravnc_dsm_helper relay 8080 ipv6.beijing2008.cn:80\n" | 
|  | "\n" | 
|  | "\n" | 
|  | "       SSL Show Certificate mode:  Set the cipher to 'showcert' to fetch\n" | 
|  | "       the SSL certificate from remotehost:port and print it to the stdout.\n" | 
|  | "       No certificate authentication or verification is performed.  E.g.\n" | 
|  | "\n" | 
|  | "         ultravnc_dsm_helper showcert www.verisign.com:443\n" | 
|  | "\n" | 
|  | "       (the output resembles that of 'openssl s_client ...')  Set the env var\n" | 
|  | "       ULTRAVNC_DSM_HELPER_SHOWCERT_ADH=1 for Anonymous Diffie Hellman mode.\n" | 
|  | "\n" | 
|  | "\n" | 
|  | "       Looping Mode:  Set the env. var. ULTRAVNC_DSM_HELPER_LOOP=1 to have it\n" | 
|  | "       restart itself after every disconnection in an endless loop.  It pauses\n" | 
|  | "       500 msec before restarting.  Use ULTRAVNC_DSM_HELPER_LOOP=N to set the\n" | 
|  | "       pause to N msec.\n" | 
|  | "\n" | 
|  | "       You can also set the env. var. ULTRAVNC_DSM_HELPER_BG to have the\n" | 
|  | "       program fork into the background for each connection, thereby acting\n" | 
|  | "       as a simple daemon.\n" | 
|  | ; | 
|  |  | 
|  | /* | 
|  | * We can also run as a module included into x11vnc (-enc option) | 
|  | * The includer must set ENC_MODULE and ENC_HAVE_OPENSSL. | 
|  | * | 
|  | * Note that when running as a module we still assume we have been | 
|  | * forked off of the parent process and are communicating back to it | 
|  | * via a socket.  So we *still* exit(3) at the end or on error.  And | 
|  | * the global settings won't work. | 
|  | */ | 
|  | #ifdef ENC_MODULE | 
|  | #  define main __enc_main | 
|  | static char *prog = "enc_helper"; | 
|  | #else | 
|  | #  define ENC_HAVE_OPENSSL 1 | 
|  | static char *prog = "ultravnc_dsm_helper"; | 
|  | #endif | 
|  |  | 
|  | /* unix includes */ | 
|  | #include <sys/types.h> | 
|  | #include <sys/stat.h> | 
|  | #include <unistd.h> | 
|  | #include <stdlib.h> | 
|  | #include <unistd.h> | 
|  | #include <stdio.h> | 
|  | #include <fcntl.h> | 
|  |  | 
|  | #include <string.h> | 
|  | #include <errno.h> | 
|  | #include <signal.h> | 
|  |  | 
|  | #include <sys/socket.h> | 
|  | #include <netinet/in.h> | 
|  | #include <netinet/tcp.h> | 
|  | #include <arpa/inet.h> | 
|  | #include <netdb.h> | 
|  |  | 
|  |  | 
|  | /* Solaris (sysv?) needs INADDR_NONE */ | 
|  | #ifndef INADDR_NONE | 
|  | #define INADDR_NONE ((in_addr_t) 0xffffffff) | 
|  | #endif | 
|  |  | 
|  | /* openssl includes */ | 
|  | #if ENC_HAVE_OPENSSL | 
|  | #include <openssl/evp.h> | 
|  | #include <openssl/rand.h> | 
|  | #include <openssl/rsa.h> | 
|  | #include <openssl/err.h> | 
|  | #include <openssl/ssl.h> | 
|  | #include <openssl/rsa.h> | 
|  | static const EVP_CIPHER *Cipher; | 
|  | static const EVP_MD *Digest; | 
|  | #endif | 
|  |  | 
|  | static char *cipher = NULL;	/* name of cipher, e.g. "aesv2" */ | 
|  | static int reverse = 0;		/* listening connection */ | 
|  | static int msrc4_sc = 0;	/* enables workaround for SC I/II */ | 
|  | static int noultra = 0;		/* manage salt/iv differently from ultradsm */ | 
|  | static int nomd = 0;		/* use the keydata directly, no md5 or salt */ | 
|  | static int pw_in = 0;		/* pw=.... read in */ | 
|  |  | 
|  |  | 
|  | /* The data that was read in from key file (or pw=password) */ | 
|  | static char keydata[1024]; | 
|  | static int keydata_len; | 
|  |  | 
|  | /* Size of salt and IV; based on UltraVNC DSM */ | 
|  | #define SALT 16 | 
|  | #define MSRC4_SALT 11 | 
|  | #define IVEC 16 | 
|  |  | 
|  | /* Set default values of salt and IV */ | 
|  | static int salt_size = SALT; | 
|  | static int ivec_size = IVEC; | 
|  |  | 
|  | /* To track parent and child pids */ | 
|  | static pid_t parent, child; | 
|  |  | 
|  | /* transfer buffer size */ | 
|  | #define BSIZE 8192 | 
|  |  | 
|  | /* Some very verbose debugging stuff I enable for testing */ | 
|  | #ifdef DBG | 
|  | #  include "dbg.h" | 
|  | #else | 
|  | #  define DEC_CT_DBG(p, n) | 
|  | #  define DEC_PT_DBG(p, n) | 
|  | #  define ENC_CT_DBG(p, n) | 
|  | #  define ENC_PT_DBG(p, n) | 
|  | #  define PRINT_IVEC | 
|  | #  define PRINT_KEYDATA | 
|  | #  define PRINT_KEYSTR_AND_FRIENDS | 
|  | #  define PRINT_LOOP_DBG1 | 
|  | #  define PRINT_LOOP_DBG2 | 
|  | #  define PRINT_LOOP_DBG3 | 
|  | #endif | 
|  |  | 
|  | /* SecureVNCPlugin from: http://adamwalling.com/SecureVNC/ */ | 
|  | #define SECUREVNC_RSA_PUBKEY_SIZE 270 | 
|  | #define SECUREVNC_ENCRYPTED_KEY_SIZE 256 | 
|  | #define SECUREVNC_SIGNATURE_SIZE 256 | 
|  | #define SECUREVNC_KEY_SIZE 16 | 
|  | #define SECUREVNC_RESERVED_SIZE 4 | 
|  | #define SECUREVNC_RC4_DROP_BYTES 3072 | 
|  | #define SECUREVNC_RAND_KEY_SOURCE 1024 | 
|  | static int securevnc = 0; | 
|  | static int securevnc_arc4 = 0; | 
|  | static char *securevnc_file = NULL; | 
|  |  | 
|  | static void enc_connections(int, char*, int); | 
|  |  | 
|  | #if !ENC_HAVE_OPENSSL | 
|  |  | 
|  | /* In case we are a module and there is no OpenSSL buildtime support */ | 
|  |  | 
|  | extern void enc_do(char *ciph, char *keyfile, char *lport, char *rhp) { | 
|  | fprintf(stderr, "%s: not compiled with OpenSSL\n", prog); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | #else | 
|  |  | 
|  | #if defined(NO_EVP_aes_256_cfb) || (defined (__SVR4) && defined (__sun) && !defined(EVP_aes_256_cfb) && !defined(ASSUME_EVP_aes_256_cfb)) | 
|  | /* | 
|  | * For Solaris 10 missing 192 & 256 bit crypto. | 
|  | * Note that EVP_aes_256_cfb is a macro. | 
|  | */ | 
|  | #undef EVP_aes_256_cfb | 
|  | #define EVP_aes_256_cfb() EVP_aes_128_cfb(); {fprintf(stderr, "Not compiled with EVP_aes_256_cfb() 'aes256' support.\n"); exit(1);} | 
|  | #endif | 
|  |  | 
|  | /* If we are a module, enc_do() is the only interface we export.  */ | 
|  |  | 
|  |  | 
|  | /* This works out key type & etc., reads key, calls enc_connections */ | 
|  |  | 
|  | extern void enc_do(char *ciph, char *keyfile, char *lport, char *rhp) { | 
|  |  | 
|  | struct stat sb; | 
|  | char *q, *p, *connect_host; | 
|  | char tmp[16]; | 
|  | int fd, len = 0, listen_port = 0, connect_port, mbits; | 
|  |  | 
|  | q = ciph; | 
|  |  | 
|  | /* check for noultra mode: */ | 
|  | if (strstr(q, "noultra:") == q) { | 
|  | noultra = 1; | 
|  | q += strlen("noultra:"); | 
|  | } | 
|  |  | 
|  | /* check for reverse mode: */ | 
|  | if (strstr(q, "rev:") == q) { | 
|  | reverse = 1; | 
|  | q += strlen("rev:"); | 
|  | } | 
|  |  | 
|  | /* work out which cipher and set Cipher to the selected one. */ | 
|  | if (!strcasecmp(q, "msrc4")) { | 
|  | Cipher = EVP_rc4();		cipher = "msrc4"; | 
|  |  | 
|  | } else if (!strcasecmp(q, "msrc4_sc")) { | 
|  | Cipher = EVP_rc4();		cipher = "msrc4"; | 
|  | msrc4_sc = 1;			/* no salt/iv workaround */ | 
|  |  | 
|  | } else if (strstr(q, "arc4") == q) { | 
|  | Cipher = EVP_rc4();		cipher = "arc4"; | 
|  |  | 
|  | } else if (strstr(q, "aesv2") == q || strstr(q, "aes-ofb") == q) { | 
|  | Cipher = EVP_aes_128_ofb();	cipher = "aesv2"; | 
|  |  | 
|  | } else if (strstr(q, "aes-cfb") == q) { | 
|  | Cipher = EVP_aes_128_cfb();	cipher = "aes-cfb"; | 
|  |  | 
|  | } else if (strstr(q, "aes256") == q) { | 
|  | Cipher = EVP_aes_256_cfb();	cipher = "aes256"; | 
|  |  | 
|  | } else if (strstr(q, "blowfish") == q) { | 
|  | Cipher = EVP_bf_cfb();		cipher = "blowfish"; | 
|  |  | 
|  | } else if (strstr(q, "3des") == q) { | 
|  | Cipher = EVP_des_ede3_cfb();	cipher = "3des"; | 
|  |  | 
|  | } else if (strstr(q, "securevnc") == q) { | 
|  | Cipher = EVP_aes_128_ofb();	cipher = "securevnc"; | 
|  | securevnc = 1; | 
|  |  | 
|  | } else if (strstr(q, "none") == q || strstr(q, "relay") == q) { | 
|  | cipher = "none"; | 
|  |  | 
|  | } else if (strstr(q, "showcert") == q) { | 
|  | cipher = "showcert"; | 
|  |  | 
|  | } else if (strstr(q, ".") == q) { | 
|  | /* otherwise, try to guess cipher from key filename: */ | 
|  | if (strstr(keyfile, "arc4.key")) { | 
|  | Cipher = EVP_rc4();		cipher = "arc4"; | 
|  |  | 
|  | } else if (strstr(keyfile, "rc4.key")) { | 
|  | Cipher = EVP_rc4();		cipher = "msrc4"; | 
|  |  | 
|  | } else if (strstr(keyfile, "aesv2.key")) { | 
|  | Cipher = EVP_aes_128_ofb();	cipher = "aesv2"; | 
|  |  | 
|  | } else if (strstr(keyfile, "aes-cfb.key")) { | 
|  | Cipher = EVP_aes_128_cfb();	cipher = "aes-cfb"; | 
|  |  | 
|  | } else if (strstr(keyfile, "aes256.key")) { | 
|  | Cipher = EVP_aes_256_cfb();	cipher = "aes256"; | 
|  |  | 
|  | } else if (strstr(keyfile, "blowfish.key")) { | 
|  | Cipher = EVP_bf_cfb();		cipher = "blowfish"; | 
|  |  | 
|  | } else if (strstr(keyfile, "3des.key")) { | 
|  | Cipher = EVP_des_ede3_cfb();	cipher = "3des"; | 
|  |  | 
|  | } else if (strstr(keyfile, "securevnc.")) { | 
|  | Cipher = EVP_aes_128_ofb();	cipher = "securevnc"; | 
|  | securevnc = 1; | 
|  |  | 
|  | } else { | 
|  | fprintf(stderr, "cannot figure out cipher, supply 'msrc4', 'arc4', or 'aesv2' ...\n"); | 
|  | exit(1); | 
|  | } | 
|  | } else { | 
|  | fprintf(stderr, "cannot figure out cipher, supply 'msrc4', 'arc4', or 'aesv2' ...\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | /* set the default message digest (md5) */ | 
|  | if (!securevnc) { | 
|  | Digest = EVP_md5(); | 
|  | } else { | 
|  | Digest = EVP_sha1(); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Look for user specified salt and IV sizes at the end | 
|  | * ( ciph@salt,iv and ciph@[md+]salt,iv ): | 
|  | */ | 
|  | p = strchr(q, '@'); | 
|  | if (p) { | 
|  | int s, v; | 
|  | p++; | 
|  | if (strstr(p, "md5+") == p) { | 
|  | Digest = EVP_md5();        p += strlen("md5+"); | 
|  | } else if (strstr(p, "sha+") == p) { | 
|  | Digest = EVP_sha();        p += strlen("sha+"); | 
|  | } else if (strstr(p, "sha1+") == p) { | 
|  | Digest = EVP_sha1();       p += strlen("sha1+"); | 
|  | } else if (strstr(p, "ripe+") == p) { | 
|  | Digest = EVP_ripemd160();  p += strlen("ripe+"); | 
|  | } else if (strstr(p, "ripemd160+") == p) { | 
|  | Digest = EVP_ripemd160();  p += strlen("ripemd160+"); | 
|  | } | 
|  | if (sscanf(p, "%d,%d", &s, &v) == 2) { | 
|  | /* cipher@n,m */ | 
|  | if (-1 <= s && s <= SALT) { | 
|  | salt_size = s; | 
|  | } else { | 
|  | fprintf(stderr, "%s: invalid salt size: %d\n", | 
|  | prog, s); | 
|  | exit(1); | 
|  | } | 
|  | if (0 <= v && v <= EVP_MAX_IV_LENGTH) { | 
|  | ivec_size = v; | 
|  | } else { | 
|  | fprintf(stderr, "%s: invalid IV size: %d\n", | 
|  | prog, v); | 
|  | exit(1); | 
|  | } | 
|  | } else if (sscanf(p, "%d", &s) == 1) { | 
|  | /* cipher@n */ | 
|  | if (-1 <= s && s <= SALT) { | 
|  | salt_size = s; | 
|  | } else { | 
|  | fprintf(stderr, "%s: invalid salt size: %d\n", | 
|  | prog, s); | 
|  | exit(1); | 
|  | } | 
|  | } | 
|  | if (salt_size == -1) { | 
|  | /* let salt = -1 mean skip both MD5 and salt */ | 
|  | nomd = 1; | 
|  | salt_size = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* port to listen on (0 => stdio, negative => localhost) */ | 
|  | if (lport != NULL) { | 
|  | listen_port = atoi(lport); | 
|  | } | 
|  |  | 
|  | /* extract remote hostname and port */ | 
|  | q = strrchr(rhp, ':'); | 
|  | if (q) { | 
|  | connect_port = atoi(q+1); | 
|  | *q = '\0'; | 
|  | } else { | 
|  | /* otherwise guess VNC display 0 ... */ | 
|  | connect_port = 5900; | 
|  | } | 
|  | connect_host = strdup(rhp); | 
|  |  | 
|  | /* check for and read in the key file */ | 
|  | memset(keydata, 0, sizeof(keydata)); | 
|  |  | 
|  | if (!strcmp(cipher, "none")) { | 
|  | goto readed_in; | 
|  | } | 
|  | if (!strcmp(cipher, "showcert")) { | 
|  | goto readed_in; | 
|  | } | 
|  |  | 
|  | if (securevnc) { | 
|  | /* note the keyfile for rsa verification later */ | 
|  | if (keyfile != NULL && strcasecmp(keyfile, "none")) { | 
|  | securevnc_file = keyfile; | 
|  | } | 
|  | goto readed_in; | 
|  | } | 
|  |  | 
|  | if (stat(keyfile, &sb) != 0) { | 
|  | if (strstr(keyfile, "pw=") == keyfile) { | 
|  | /* user specified key/password on cmdline */ | 
|  | int i; | 
|  | len = 0; | 
|  | pw_in = 1; | 
|  | for (i=0; i < (int) strlen(keyfile); i++) { | 
|  | /* load the string to keydata: */ | 
|  | int n = i + strlen("pw="); | 
|  | keydata[i] = keyfile[n]; | 
|  | if (keyfile[n] == '\0') break; | 
|  | len++; | 
|  | if (i > 100) break; | 
|  | } | 
|  | goto readed_in; | 
|  | } | 
|  | /* otherwise invalid file */ | 
|  | perror("stat"); | 
|  | exit(1); | 
|  | } | 
|  | if (sb.st_size > 1024) { | 
|  | fprintf(stderr, "%s: key file too big.\n", prog); | 
|  | exit(1); | 
|  | } | 
|  | fd = open(keyfile, O_RDONLY); | 
|  | if (fd < 0) { | 
|  | perror("open"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | /* read it all in */ | 
|  | len = (int) read(fd, keydata, (size_t) sb.st_size); | 
|  | if (len != sb.st_size) { | 
|  | perror("read"); | 
|  | fprintf(stderr, "%s, could not read key file.\n", prog); | 
|  | exit(1); | 
|  | } | 
|  | close(fd); | 
|  |  | 
|  | readed_in: | 
|  |  | 
|  |  | 
|  | /* check for ultravnc msrc4 format 'rc4.key' */ | 
|  | mbits = 0; | 
|  | if (strstr(keydata, "128 bit") == keydata) { | 
|  | mbits = 128; | 
|  | } else if (strstr(keydata, " 56 bit") == keydata) { | 
|  | mbits = 56; | 
|  | } else if (strstr(keydata, " 40 bit") == keydata) { | 
|  | mbits = 40; | 
|  | } | 
|  | if (mbits > 0) { | 
|  | /* 4 is for int key length, 12 is for BLOBHEADER. */ | 
|  | int i, offset = strlen("xxx bit") + 4 + 12; | 
|  |  | 
|  | /* the key is stored in reverse order! */ | 
|  | len = mbits/8; | 
|  | for (i=0; i < len; i++) { | 
|  | tmp[i] = keydata[offset + len - i - 1]; | 
|  | } | 
|  |  | 
|  | /* clear keydata and then copy the reversed bytes there: */ | 
|  | memset(keydata, 0, sizeof(keydata)); | 
|  | memcpy(keydata, tmp, len); | 
|  | } | 
|  |  | 
|  | keydata_len = len; | 
|  |  | 
|  | /* initialize random */ | 
|  | RAND_poll(); | 
|  |  | 
|  | /* | 
|  | * Setup connections, then transfer data when they are all | 
|  | * hooked up. | 
|  | */ | 
|  | enc_connections(listen_port, connect_host, connect_port); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static void enc_raw_xfer(int sock_fr, int sock_to) { | 
|  |  | 
|  | unsigned char buf[BSIZE]; | 
|  | unsigned char *psrc = NULL; | 
|  | int len, m, n = 0; | 
|  |  | 
|  | /* zero the buffers */ | 
|  | memset(buf, 0, BSIZE); | 
|  |  | 
|  | /* now loop forever processing the data stream */ | 
|  | while (1) { | 
|  | errno = 0; | 
|  |  | 
|  | /* general case of loop, read some in: */ | 
|  | n = read(sock_fr, buf, BSIZE); | 
|  |  | 
|  | if (n == 0 || (n < 0 && errno != EINTR)) { | 
|  | /* failure to read any data, it is EOF or fatal error */ | 
|  | int err = errno; | 
|  |  | 
|  | /* debug output: */ | 
|  | fprintf(stderr, "%s: input stream finished: n=%d, err=%d", prog, n, err); | 
|  |  | 
|  | /* EOF or fatal error */ | 
|  | break; | 
|  |  | 
|  | } else if (n > 0) { | 
|  |  | 
|  | /* write data to the other end: */ | 
|  | len = n; | 
|  | psrc = buf; | 
|  | while (len > 0) { | 
|  | errno = 0; | 
|  | m = write(sock_to, psrc, len); | 
|  |  | 
|  | if (m > 0) { | 
|  | /* scoot them by how much was written: */ | 
|  | psrc += m; | 
|  | len  -= m; | 
|  | } | 
|  | if (m < 0 && (errno == EINTR || errno == EAGAIN)) { | 
|  | /* interrupted or blocked */ | 
|  | continue; | 
|  | } | 
|  | /* EOF or fatal error */ | 
|  | break; | 
|  | } | 
|  | } else { | 
|  | /* this is EINTR */ | 
|  | } | 
|  | } | 
|  |  | 
|  | /* transfer done (viewer exited or some error) */ | 
|  |  | 
|  | fprintf(stderr, "\n%s: close sock_to\n", prog); | 
|  | close(sock_to); | 
|  |  | 
|  | fprintf(stderr,   "%s: close sock_fr\n", prog); | 
|  | close(sock_fr); | 
|  |  | 
|  | /* kill our partner after 1 secs. */ | 
|  | sleep(1); | 
|  | if (child)  { | 
|  | if (kill(child, SIGTERM) == 0) { | 
|  | fprintf(stderr, "%s[%d]: killed my partner: %d\n", | 
|  | prog, (int) getpid(), (int) child); | 
|  | } | 
|  | } else { | 
|  | if (kill(parent, SIGTERM) == 0) { | 
|  | fprintf(stderr, "%s[%d]: killed my partner: %d\n", | 
|  | prog, (int) getpid(), (int) parent); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #if ENC_HAVE_OPENSSL | 
|  | /* | 
|  | * Initialize cipher context and then loop till EOF doing transfer & | 
|  | * encrypt or decrypt. | 
|  | */ | 
|  | static void enc_xfer(int sock_fr, int sock_to, int encrypt) { | 
|  | /* | 
|  | * We keep both E and D aspects in case we revert back to a | 
|  | * single process calling select(2) on all fds... | 
|  | */ | 
|  | unsigned char E_keystr[EVP_MAX_KEY_LENGTH]; | 
|  | unsigned char D_keystr[EVP_MAX_KEY_LENGTH]; | 
|  | EVP_CIPHER_CTX E_ctx, D_ctx; | 
|  | EVP_CIPHER_CTX *ctx = NULL; | 
|  |  | 
|  | unsigned char buf[BSIZE], out[BSIZE]; | 
|  | unsigned char *psrc = NULL, *keystr; | 
|  | unsigned char salt[SALT+1]; | 
|  | unsigned char ivec_real[EVP_MAX_IV_LENGTH]; | 
|  | unsigned char *ivec = ivec_real; | 
|  |  | 
|  | int i, cnt, len, m, n = 0, vb = 0, first = 1; | 
|  | int whoops = 1; /* for the msrc4 problem */ | 
|  | char *encstr, *encsym; | 
|  |  | 
|  | /* zero the buffers */ | 
|  | memset(buf,  0, BSIZE); | 
|  | memset(out,  0, BSIZE); | 
|  | memset(salt, 0, sizeof(salt)); | 
|  | memset(ivec_real, 0, sizeof(ivec_real)); | 
|  | memset(E_keystr, 0, sizeof(E_keystr)); | 
|  | memset(D_keystr, 0, sizeof(D_keystr)); | 
|  |  | 
|  | if (!strcmp(cipher, "msrc4")) { | 
|  | salt_size = MSRC4_SALT; /* 11 vs. 16 */ | 
|  | } | 
|  |  | 
|  | if (msrc4_sc) { | 
|  | whoops = 1;	/* force workaround in SC mode */ | 
|  | } | 
|  |  | 
|  | if (getenv("ENCRYPT_VERBOSE")) { | 
|  | vb = 1;	/* let user turn on some debugging via env. var. */ | 
|  | } | 
|  |  | 
|  | /* | 
|  | * reverse mode, e.g. we help a vnc server instead of a viewer. | 
|  | */ | 
|  | if (reverse) { | 
|  | encrypt = (!encrypt); | 
|  | } | 
|  | encstr = encrypt ? "encrypt" : "decrypt";  /* string for messages */ | 
|  | encsym = encrypt ? "+" : "-"; | 
|  |  | 
|  | /* use the encryption/decryption context variables below */ | 
|  | if (encrypt) { | 
|  | ctx = &E_ctx; | 
|  | keystr = E_keystr; | 
|  | } else { | 
|  | ctx = &D_ctx; | 
|  | keystr = D_keystr; | 
|  | } | 
|  |  | 
|  | if (securevnc) { | 
|  | first = 0;	/* no need for salt+iv on first time */ | 
|  | salt_size = 0;	/* we want no salt */ | 
|  | n = 0;		/* nothing read */ | 
|  | ivec_size = 0;	/* we want no IV. */ | 
|  | ivec = NULL; | 
|  | } else if (encrypt) { | 
|  | /* encrypter initializes the salt and initialization vector */ | 
|  |  | 
|  | /* | 
|  | * Our salt is 16 bytes but I believe only the first 8 | 
|  | * bytes are used by EVP_BytesToKey(3).  Since we send it | 
|  | * to the other "plugin" we need to keep it 16.  Also, | 
|  | * the IV size can depend on the cipher type.  Again, 16. | 
|  | */ | 
|  | RAND_bytes(salt, salt_size); | 
|  | RAND_bytes(ivec, ivec_size); | 
|  |  | 
|  | /* place them in the send buffer: */ | 
|  | memcpy(buf, salt, salt_size); | 
|  | memcpy(buf+salt_size, ivec, ivec_size); | 
|  |  | 
|  | n = salt_size + ivec_size; | 
|  |  | 
|  | ENC_PT_DBG(buf, n); | 
|  |  | 
|  | } else { | 
|  | /* decrypter needs to read salt + iv from the wire: */ | 
|  |  | 
|  | /* sleep 100 ms (TODO: select on fd) */ | 
|  | struct timeval tv; | 
|  | tv.tv_sec  = 0; | 
|  | tv.tv_usec = 100 * 1000; | 
|  | select(1, NULL, NULL, NULL, &tv); | 
|  |  | 
|  | if (salt_size+ivec_size == 0) { | 
|  | n = 0;	/* no salt or iv, skip reading. */ | 
|  | } else { | 
|  | n = read(sock_fr, buf, salt_size+ivec_size+96); | 
|  | } | 
|  | if (n == 0 && salt_size+ivec_size > 0) { | 
|  | fprintf(stderr, "%s: decrypt finished.\n", prog); | 
|  | goto finished; | 
|  | } | 
|  | if (n < salt_size+ivec_size) { | 
|  | if (msrc4_sc && n == 12) { | 
|  | fprintf(stderr, "%s: only %d bytes read. Assuming " | 
|  | "UVNC Single Click server.\n", prog, n); | 
|  | } else { | 
|  | if (n < 0) perror("read"); | 
|  | fprintf(stderr, "%s: could not read enough for salt " | 
|  | "and ivec: n=%d\n", prog, n); | 
|  | goto finished; | 
|  | } | 
|  | } | 
|  |  | 
|  | DEC_CT_DBG(buf, n); | 
|  |  | 
|  | if (msrc4_sc && n == 12) { | 
|  | ; /* send it as is */ | 
|  | } else { | 
|  | /* extract them to their buffers: */ | 
|  | memcpy(salt, buf, salt_size); | 
|  | memcpy(ivec, buf+salt_size, ivec_size); | 
|  |  | 
|  | /* the rest is some encrypted data: */ | 
|  | n = n - salt_size - ivec_size; | 
|  | psrc = buf + salt_size + ivec_size; | 
|  |  | 
|  | if (n > 0) { | 
|  | /* | 
|  | * copy it down to the start of buf for | 
|  | * sending below: | 
|  | */ | 
|  | for (i=0; i < n; i++) { | 
|  | buf[i] = psrc[i]; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* debug output */ | 
|  | PRINT_KEYDATA; | 
|  | PRINT_IVEC; | 
|  |  | 
|  | if (!strcmp(cipher, "msrc4")) { | 
|  | /* special cases for MSRC4: */ | 
|  |  | 
|  | if (whoops) { | 
|  | fprintf(stderr, "%s: %s - WARNING: MSRC4 mode and IGNORING random salt\n", prog, encstr); | 
|  | fprintf(stderr, "%s: %s - WARNING: and initialization vector!!\n", prog, encstr); | 
|  | EVP_CIPHER_CTX_init(ctx); | 
|  | if (pw_in) { | 
|  | /* for pw=xxxx a md5 hash is used */ | 
|  | EVP_BytesToKey(Cipher, Digest, NULL, (unsigned char *) keydata, | 
|  | keydata_len, 1, keystr, NULL); | 
|  | EVP_CipherInit_ex(ctx, Cipher, NULL, keystr, NULL, | 
|  | encrypt); | 
|  | } else { | 
|  | /* otherwise keydata as is */ | 
|  | EVP_CipherInit_ex(ctx, Cipher, NULL, | 
|  | (unsigned char *) keydata, NULL, encrypt); | 
|  | } | 
|  | } else { | 
|  | /* XXX might not be correct, just exit. */ | 
|  | fprintf(stderr, "%s: %s - Not sure about msrc4 && !whoops case, exiting.\n", prog, encstr); | 
|  | exit(1); | 
|  |  | 
|  | EVP_BytesToKey(Cipher, Digest, NULL, (unsigned char *) keydata, | 
|  | keydata_len, 1, keystr, ivec); | 
|  | EVP_CIPHER_CTX_init(ctx); | 
|  | EVP_CipherInit_ex(ctx, Cipher, NULL, keystr, ivec, | 
|  | encrypt); | 
|  | } | 
|  |  | 
|  | } else { | 
|  | unsigned char *in_salt = NULL; | 
|  |  | 
|  | /* check salt and IV source and size. */ | 
|  | if (securevnc) { | 
|  | in_salt = NULL; | 
|  | } else if (salt_size <= 0) { | 
|  | /* let salt_size = 0 mean keep it out of the MD5 */ | 
|  | fprintf(stderr, "%s: %s - WARNING: no salt\n", | 
|  | prog, encstr); | 
|  | in_salt = NULL; | 
|  | } else { | 
|  | in_salt = salt; | 
|  | } | 
|  |  | 
|  | if (ivec_size < Cipher->iv_len && !securevnc) { | 
|  | fprintf(stderr, "%s: %s - WARNING: short IV %d < %d\n", | 
|  | prog, encstr, ivec_size, Cipher->iv_len); | 
|  | } | 
|  |  | 
|  | /* make the hashed value and place in keystr */ | 
|  |  | 
|  | /* | 
|  | * XXX N.B.: DSM plugin had count=0, and overwrote ivec | 
|  | * by not passing NULL iv. | 
|  | */ | 
|  |  | 
|  | if (nomd) { | 
|  | /* special mode: no salt or md5, use keydata directly */ | 
|  |  | 
|  | int sz = keydata_len < EVP_MAX_KEY_LENGTH ? | 
|  | keydata_len : EVP_MAX_KEY_LENGTH; | 
|  |  | 
|  | fprintf(stderr, "%s: %s - WARNING: no-md5 specified: ignoring salt & hash\n", prog, encstr); | 
|  | memcpy(keystr, keydata, sz); | 
|  |  | 
|  | } else if (noultra && ivec_size > 0) { | 
|  | /* "normal" mode, don't overwrite ivec. */ | 
|  |  | 
|  | EVP_BytesToKey(Cipher, Digest, in_salt, (unsigned char *) keydata, | 
|  | keydata_len, 1, keystr, NULL); | 
|  |  | 
|  | } else { | 
|  | /* | 
|  | * Ultra DSM compatibility mode.  Note that this | 
|  | * clobbers the ivec we set up above!  Under | 
|  | * noultra we overwrite ivec only if ivec_size=0. | 
|  | * | 
|  | * SecureVNC also goes through here. in_salt and ivec are NULL. | 
|  | * And ivec is NULL below in the EVP_CipherInit_ex() call. | 
|  | */ | 
|  | EVP_BytesToKey(Cipher, Digest, in_salt, (unsigned char *) keydata, | 
|  | keydata_len, 1, keystr, ivec); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* initialize the context */ | 
|  | EVP_CIPHER_CTX_init(ctx); | 
|  |  | 
|  |  | 
|  | /* set the cipher & initialize */ | 
|  |  | 
|  | /* | 
|  | * XXX N.B.: DSM plugin implementation had encrypt=1 | 
|  | * for both (i.e. perfectly symmetric) | 
|  | */ | 
|  |  | 
|  | EVP_CipherInit_ex(ctx, Cipher, NULL, keystr, ivec, encrypt); | 
|  | } | 
|  |  | 
|  | if (securevnc && securevnc_arc4) { | 
|  | /* need to discard initial 3072 bytes */ | 
|  | unsigned char buf1[SECUREVNC_RC4_DROP_BYTES]; | 
|  | unsigned char buf2[SECUREVNC_RC4_DROP_BYTES]; | 
|  | int cnt = 0; | 
|  | EVP_CipherUpdate(ctx, buf1, &cnt, buf2, SECUREVNC_RC4_DROP_BYTES); | 
|  | } | 
|  |  | 
|  | /* debug output */ | 
|  | PRINT_KEYSTR_AND_FRIENDS; | 
|  |  | 
|  | /* now loop forever processing the data stream */ | 
|  |  | 
|  | while (1) { | 
|  | errno = 0; | 
|  | if (first && n > 0) { | 
|  | if (encrypt && msrc4_sc) { | 
|  | /* skip sending salt+iv */ | 
|  | first = 0; | 
|  | continue; | 
|  | } else { | 
|  | /* use that first block of data placed in buf */ | 
|  | } | 
|  | } else if (first && n == 0 && salt_size + ivec_size == 0) { | 
|  | first = 0; | 
|  | continue; | 
|  | } else { | 
|  | /* general case of loop, read some in: */ | 
|  | n = read(sock_fr, buf, BSIZE); | 
|  | } | 
|  |  | 
|  | /* debug output: */ | 
|  | if (vb) fprintf(stderr, "%s%d/%d ", encsym, n, errno); | 
|  | PRINT_LOOP_DBG1; | 
|  |  | 
|  | if (n == 0 || (n < 0 && errno != EINTR)) { | 
|  | /* failure to read any data, it is EOF or fatal error */ | 
|  | int err = errno; | 
|  |  | 
|  | /* debug output: */ | 
|  | PRINT_LOOP_DBG2; | 
|  | fprintf(stderr, "%s: %s - input stream finished: n=%d, err=%d", prog, encstr, n, err); | 
|  |  | 
|  | /* EOF or fatal error */ | 
|  | break; | 
|  |  | 
|  | } else if (n > 0) { | 
|  | /* we read in some data, now transform it: */ | 
|  |  | 
|  | if (first && encrypt) { | 
|  | /* first time, copy the salt and ivec to out[] for sending */ | 
|  | memcpy(out, buf, n); | 
|  | cnt = n; | 
|  |  | 
|  | } else if (!EVP_CipherUpdate(ctx, out, &cnt, buf, n)) { | 
|  | /* otherwise, we transform the data */ | 
|  | fprintf(stderr, "%s: enc_xfer EVP_CipherUpdate failed.\n", prog); | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* debug output: */ | 
|  | if (vb) fprintf(stderr, "%sc%d/%d ", encsym, cnt, n); | 
|  | PRINT_LOOP_DBG3; | 
|  |  | 
|  | /* write transformed data to the other end: */ | 
|  | len = cnt; | 
|  | psrc = out; | 
|  | while (len > 0) { | 
|  | errno = 0; | 
|  | m = write(sock_to, psrc, len); | 
|  |  | 
|  | /* debug output: */ | 
|  | if (vb) fprintf(stderr, "m%s%d/%d ", encsym, m, errno); | 
|  |  | 
|  | if (m > 0) { | 
|  | /* scoot them by how much was written: */ | 
|  | psrc += m; | 
|  | len  -= m; | 
|  | } | 
|  | if (m < 0 && (errno == EINTR || errno == EAGAIN)) { | 
|  | /* interrupted or blocked */ | 
|  | continue; | 
|  | } | 
|  | /* EOF or fatal error */ | 
|  | break; | 
|  | } | 
|  | } else { | 
|  | /* this is EINTR */ | 
|  | } | 
|  | first = 0; | 
|  | } | 
|  |  | 
|  | /* transfer done (viewer exited or some error) */ | 
|  | finished: | 
|  |  | 
|  | fprintf(stderr, "\n%s: %s - close sock_to\n", prog, encstr); | 
|  | close(sock_to); | 
|  |  | 
|  | fprintf(stderr,   "%s: %s - close sock_fr\n", prog, encstr); | 
|  | close(sock_fr); | 
|  |  | 
|  | /* kill our partner after 2 secs. */ | 
|  | sleep(2); | 
|  | if (child)  { | 
|  | if (kill(child, SIGTERM) == 0) { | 
|  | fprintf(stderr, "%s[%d]: %s - killed my partner: %d\n", | 
|  | prog, (int) getpid(), encstr, (int) child); | 
|  | } | 
|  | } else { | 
|  | if (kill(parent, SIGTERM) == 0) { | 
|  | fprintf(stderr, "%s[%d]: %s - killed my partner: %d\n", | 
|  | prog, (int) getpid(), encstr, (int) parent); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static int securevnc_server_rsa_save_dialog(char *file, char *md5str, unsigned char* rsabuf) { | 
|  | /* since we are likely running in the background, use this kludge by running tk */ | 
|  | FILE *p; | 
|  | char str[2], *q = file, *cmd = getenv("WISH") ? getenv("WISH") : "wish"; | 
|  | int rc; | 
|  |  | 
|  | memset(str, 0, sizeof(str)); | 
|  |  | 
|  | p = popen(cmd, "w"); | 
|  | if (p == NULL) { | 
|  | fprintf(stderr, "checkserver_rsa: could not run: %s\n", cmd); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* start piping tk/tcl code to it: */ | 
|  | fprintf(p, "wm withdraw .\n"); | 
|  | fprintf(p, "set x [expr [winfo screenwidth  .]/2]\n"); | 
|  | fprintf(p, "set y [expr [winfo screenheight .]/2]\n"); | 
|  | fprintf(p, "wm geometry . +$x+$y; update\n"); | 
|  | fprintf(p, "catch {option add *Dialog.msg.font {helvetica -14 bold}}\n"); | 
|  | fprintf(p, "catch {option add *Dialog.msg.wrapLength 6i}\n"); | 
|  | fprintf(p, "set ans [tk_messageBox -title \"Save and Trust UltraVNC RSA Key?\" -icon question "); | 
|  | fprintf(p, "-type yesno -message \"Save and Trust UltraVNC SecureVNCPlugin RSA Key\\n\\n"); | 
|  | fprintf(p, "With MD5 sum: %s\\n\\n", md5str); | 
|  | fprintf(p, "In file: "); | 
|  | while (*q != '\0') { | 
|  | /* sanitize user supplied string: */ | 
|  | str[0] = *q; | 
|  | if (strpbrk(str, "[](){}`'\"$&*|<>") == NULL) { | 
|  | fprintf(p, "%s", str); | 
|  | } | 
|  | q++; | 
|  | } | 
|  | fprintf(p, " ?\"]\n"); | 
|  | fprintf(p, "if { $ans == \"yes\" } {destroy .; exit 0} else {destroy .; exit 1}\n"); | 
|  | rc = pclose(p); | 
|  | if (rc == 0) { | 
|  | fprintf(stderr, "checkserver_rsa: query returned: %d.  saving it.\n", rc); | 
|  | p = fopen(file, "w"); | 
|  | if (p == NULL) { | 
|  | fprintf(stderr, "checkserver_rsa: could not open %s\n", file); | 
|  | return 0; | 
|  | } | 
|  | write(fileno(p), rsabuf, SECUREVNC_RSA_PUBKEY_SIZE); | 
|  | fclose(p); | 
|  | return 2; | 
|  | } else { | 
|  | fprintf(stderr, "checkserver_rsa: query returned: %d.  NOT saving it.\n", rc); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | static char *rsa_md5_sum(unsigned char* rsabuf) { | 
|  | EVP_MD_CTX md; | 
|  | char digest[EVP_MAX_MD_SIZE], tmp[16]; | 
|  | char md5str[EVP_MAX_MD_SIZE * 8]; | 
|  | unsigned int i, size = 0; | 
|  |  | 
|  | EVP_DigestInit(&md, EVP_md5()); | 
|  | EVP_DigestUpdate(&md, rsabuf, SECUREVNC_RSA_PUBKEY_SIZE); | 
|  | EVP_DigestFinal(&md, (unsigned char *)digest, &size); | 
|  |  | 
|  | memset(md5str, 0, sizeof(md5str)); | 
|  | for (i=0; i < size; i++) { | 
|  | unsigned char uc = (unsigned char) digest[i]; | 
|  | sprintf(tmp, "%02x", (int) uc); | 
|  | strcat(md5str, tmp); | 
|  | } | 
|  | return strdup(md5str); | 
|  | } | 
|  |  | 
|  | static int securevnc_check_server_rsa(char *file, unsigned char *rsabuf) { | 
|  | struct stat sb; | 
|  | unsigned char filebuf[SECUREVNC_RSA_PUBKEY_SIZE]; | 
|  | char *md5str = rsa_md5_sum(rsabuf); | 
|  |  | 
|  | if (!file) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | memset(filebuf, 0, sizeof(filebuf)); | 
|  | if (stat(file, &sb) == 0) { | 
|  | int n, fd, i, ok = 1; | 
|  |  | 
|  | if (sb.st_size != SECUREVNC_RSA_PUBKEY_SIZE) { | 
|  | fprintf(stderr, "checkserver_rsa: file is wrong size: %d != %d '%s'\n", | 
|  | (int) sb.st_size, SECUREVNC_RSA_PUBKEY_SIZE, file); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | fd = open(file, O_RDONLY); | 
|  | if (fd < 0) { | 
|  | fprintf(stderr, "checkserver_rsa: could not open: '%s'\n", file); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | n = (int) read(fd, filebuf, SECUREVNC_RSA_PUBKEY_SIZE); | 
|  | close(fd); | 
|  | if (n != SECUREVNC_RSA_PUBKEY_SIZE) { | 
|  | fprintf(stderr, "checkserver_rsa: could not read all of file: %d != %d '%s'\n", | 
|  | n, SECUREVNC_RSA_PUBKEY_SIZE, file); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | for (i=0; i < SECUREVNC_RSA_PUBKEY_SIZE; i++) { | 
|  | if (filebuf[i] != rsabuf[i]) { | 
|  | ok = 0; | 
|  | } | 
|  | } | 
|  | if (!ok) { | 
|  | char *str1 = rsa_md5_sum(rsabuf); | 
|  | char *str2 = rsa_md5_sum(filebuf); | 
|  | fprintf(stderr, "checkserver_rsa: rsa keystore contents differ for '%s'\n", file); | 
|  | fprintf(stderr, "checkserver_rsa: MD5 sum of server key: %s\n", str1); | 
|  | fprintf(stderr, "checkserver_rsa: MD5 sum of keystore:   %s\n", str2); | 
|  | } | 
|  | return ok; | 
|  | } else { | 
|  |  | 
|  | fprintf(stderr, "checkserver_rsa: rsa keystore file does not exist: '%s'\n", file); | 
|  | fprintf(stderr, "checkserver_rsa: asking user if we should store rsa key in it.\n\n"); | 
|  | fprintf(stderr, "checkserver_rsa: RSA key has MD5 sum: %s\n\n", md5str); | 
|  |  | 
|  | return securevnc_server_rsa_save_dialog(file, md5str, rsabuf); | 
|  | } | 
|  | } | 
|  |  | 
|  | static RSA *load_client_auth(char *file) { | 
|  | struct stat sb; | 
|  | int fd, n; | 
|  | char *contents; | 
|  | RSA *rsa; | 
|  |  | 
|  | if (!file) { | 
|  | return NULL; | 
|  | } | 
|  | if (stat(file, &sb) != 0) { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | fd = open(file, O_RDONLY); | 
|  | if (fd < 0) { | 
|  | fprintf(stderr, "load_client_auth: could not open: '%s'\n", file); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | contents = (char *) malloc(sb.st_size); | 
|  | n = (int) read(fd, contents, sb.st_size); | 
|  | close(fd); | 
|  |  | 
|  | if (n != sb.st_size)  { | 
|  | fprintf(stderr, "load_client_auth: could not read all of: '%s'\n", file); | 
|  | free(contents); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | rsa = d2i_RSAPrivateKey(NULL, (const unsigned char **) ((void *) &contents), sb.st_size); | 
|  | if (!rsa) { | 
|  | fprintf(stderr, "load_client_auth: d2i_RSAPrivateKey failed for: '%s'\n", file); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | if (RSA_check_key(rsa) != 1) { | 
|  | fprintf(stderr, "load_client_auth: rsa key invalid: '%s'\n", file); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | return rsa; | 
|  | } | 
|  |  | 
|  | static void sslexit(char *msg) { | 
|  | fprintf(stderr, "%s: %s\n", msg, ERR_error_string(ERR_get_error(), NULL)); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | static void securevnc_setup(int conn1, int conn2) { | 
|  | RSA *rsa = NULL; | 
|  | EVP_CIPHER_CTX init_ctx; | 
|  | unsigned char keystr[EVP_MAX_KEY_LENGTH]; | 
|  | unsigned char *rsabuf, *rsasav; | 
|  | unsigned char *encrypted_keybuf; | 
|  | unsigned char *initkey; | 
|  | unsigned int server_flags = 0; | 
|  | unsigned char one = 1, zero = 0, sig = 16; | 
|  | unsigned char b1, b2, b3, b4; | 
|  | unsigned char buf[BSIZE], to_viewer[BSIZE]; | 
|  | int to_viewer_len = 0; | 
|  | int n = 0, len, rc; | 
|  | int server = reverse ? conn1 : conn2; | 
|  | int viewer = reverse ? conn2 : conn1; | 
|  | char *client_auth = NULL; | 
|  | int client_auth_req = 0; | 
|  | int keystore_verified = 0; | 
|  |  | 
|  | ERR_load_crypto_strings(); | 
|  |  | 
|  | /* alloc and read from server the 270 comprising the rsa public key: */ | 
|  | rsabuf = (unsigned char *) calloc(SECUREVNC_RSA_PUBKEY_SIZE, 1); | 
|  | rsasav = (unsigned char *) calloc(SECUREVNC_RSA_PUBKEY_SIZE, 1); | 
|  | len = 0; | 
|  | while (len < SECUREVNC_RSA_PUBKEY_SIZE) { | 
|  | n = read(server, rsabuf + len, SECUREVNC_RSA_PUBKEY_SIZE - len); | 
|  | if (n == 0 || (n < 0 && errno != EINTR)) { | 
|  | fprintf(stderr, "securevnc_setup: fail read rsabuf: n=%d len=%d\n", n, len); | 
|  | exit(1); | 
|  | } | 
|  | len += n; | 
|  | } | 
|  | if (len != SECUREVNC_RSA_PUBKEY_SIZE) { | 
|  | fprintf(stderr, "securevnc_setup: fail final read rsabuf: n=%d len=%d\n", n, len); | 
|  | exit(1); | 
|  | } | 
|  | fprintf(stderr, "securevnc_setup: rsa data read len: %d\n", len); | 
|  | memcpy(rsasav, rsabuf, SECUREVNC_RSA_PUBKEY_SIZE); | 
|  |  | 
|  | fprintf(stderr, "securevnc_setup: RSA key has MD5 sum: %s\n", rsa_md5_sum(rsabuf)); | 
|  | fprintf(stderr, "securevnc_setup:\n"); | 
|  | fprintf(stderr, "securevnc_setup: One way to print out the SecureVNC Server key MD5 sum is:\n\n"); | 
|  | fprintf(stderr, "openssl rsa -inform DER -outform DER -pubout -in ./Server_SecureVNC.pkey | dd bs=1 skip=24 | md5sum\n\n"); | 
|  | if (securevnc_file == NULL) { | 
|  | fprintf(stderr, "securevnc_setup:\n"); | 
|  | fprintf(stderr, "securevnc_setup: ** WARNING: ULTRAVNC SERVER RSA KEY NOT VERIFIED.   **\n"); | 
|  | fprintf(stderr, "securevnc_setup: ** WARNING: A MAN-IN-THE-MIDDLE ATTACK IS POSSIBLE. **\n"); | 
|  | fprintf(stderr, "securevnc_setup:\n"); | 
|  | } else { | 
|  | char *q = strrchr(securevnc_file, 'C'); | 
|  | int skip = 0; | 
|  | if (q) { | 
|  | if (!strcmp(q, "ClientAuth.pkey")) { | 
|  | client_auth = strdup(securevnc_file); | 
|  | skip = 1; | 
|  | } else if (!strcmp(q, "ClientAuth.pkey.rsa")) { | 
|  | client_auth = strdup(securevnc_file); | 
|  | q = strrchr(client_auth, '.'); | 
|  | *q = '\0'; | 
|  | } | 
|  | } | 
|  | if (!skip) { | 
|  | rc = securevnc_check_server_rsa(securevnc_file, rsabuf); | 
|  | } | 
|  | if (skip) { | 
|  | ; | 
|  | } else if (rc == 0) { | 
|  | fprintf(stderr, "securevnc_setup:\n"); | 
|  | fprintf(stderr, "securevnc_setup: VERIFY_ERROR: SERVER RSA KEY DID NOT MATCH:\n"); | 
|  | fprintf(stderr, "securevnc_setup:     %s\n", securevnc_file); | 
|  | fprintf(stderr, "securevnc_setup:\n"); | 
|  | exit(1); | 
|  | } else if (rc == -1) { | 
|  | fprintf(stderr, "securevnc_setup: User cancelled the save and hence the connection.\n"); | 
|  | fprintf(stderr, "securevnc_setup:     %s\n", securevnc_file); | 
|  | exit(1); | 
|  | } else if (rc == 1) { | 
|  | fprintf(stderr, "securevnc_setup: VERIFY SUCCESS: server rsa key matches the contents of:\n"); | 
|  | fprintf(stderr, "securevnc_setup:     %s\n", securevnc_file); | 
|  | keystore_verified = 1; | 
|  | } else if (rc == 2) { | 
|  | fprintf(stderr, "securevnc_setup: Server rsa key stored in:\n"); | 
|  | fprintf(stderr, "securevnc_setup:     %s\n", securevnc_file); | 
|  | keystore_verified = 2; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * read in the server flags. Note that SecureVNCPlugin sends these | 
|  | * in little endian and not network order!! | 
|  | */ | 
|  | read(server, (char *) &b1, 1); | 
|  | read(server, (char *) &b2, 1); | 
|  | read(server, (char *) &b3, 1); | 
|  | read(server, (char *) &b4, 1); | 
|  |  | 
|  | server_flags = 0; | 
|  | server_flags |= ((unsigned int) b4) << 24; | 
|  | server_flags |= ((unsigned int) b3) << 16; | 
|  | server_flags |= ((unsigned int) b2) << 8; | 
|  | server_flags |= ((unsigned int) b1) << 0; | 
|  | fprintf(stderr, "securevnc_setup: server_flags: 0x%08x\n", server_flags); | 
|  |  | 
|  | /* check for arc4 usage: */ | 
|  | if (server_flags & 0x1) { | 
|  | fprintf(stderr, "securevnc_setup: server uses AES cipher.\n"); | 
|  | } else { | 
|  | fprintf(stderr, "securevnc_setup: server uses ARC4 cipher.\n"); | 
|  | securevnc_arc4 = 1; | 
|  | Cipher = EVP_rc4(); | 
|  | } | 
|  |  | 
|  | /* check for client auth signature requirement: */ | 
|  | if (server_flags & (sig << 24)) { | 
|  | fprintf(stderr, "securevnc_setup: server requires Client Auth signature.\n"); | 
|  | client_auth_req = 1; | 
|  | if (!client_auth) { | 
|  | fprintf(stderr, "securevnc_setup: However, NO *ClientAuth.pkey keyfile was supplied on our\n"); | 
|  | fprintf(stderr, "securevnc_setup: command line.  Exiting.\n"); | 
|  | exit(1); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * The first packet 'RFB 003.006' is obscured with key | 
|  | * that is a sha1 hash of public key.  So make this tmp key now: | 
|  | * | 
|  | */ | 
|  | initkey = (unsigned char *) calloc(SECUREVNC_KEY_SIZE, 1); | 
|  | EVP_BytesToKey(EVP_rc4(), EVP_sha1(), NULL, rsabuf, SECUREVNC_RSA_PUBKEY_SIZE, 1, initkey, NULL); | 
|  |  | 
|  | /* expand the transported rsabuf into an rsa object */ | 
|  | rsa = d2i_RSAPublicKey(NULL, (const unsigned char **) &rsabuf, SECUREVNC_RSA_PUBKEY_SIZE); | 
|  | if (rsa == NULL) { | 
|  | sslexit("securevnc_setup: failed to create rsa"); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Back to the work involving the tmp obscuring key: | 
|  | */ | 
|  | EVP_CIPHER_CTX_init(&init_ctx); | 
|  | rc = EVP_CipherInit_ex(&init_ctx, EVP_rc4(), NULL, initkey, NULL, 1); | 
|  | if (rc == 0) { | 
|  | sslexit("securevnc_setup: EVP_CipherInit_ex(init_ctx) failed"); | 
|  | } | 
|  |  | 
|  | /* for the first obscured packet, read what we can... */ | 
|  | n = read(server, (char *) buf, BSIZE); | 
|  | fprintf(stderr, "securevnc_setup: data read: %d\n", n); | 
|  | if (n < 0) { | 
|  | exit(1); | 
|  | } | 
|  | fprintf(stderr, "securevnc_setup: initial data[%d]: ", n); | 
|  |  | 
|  | /* decode with the tmp key */ | 
|  | if (n > 0) { | 
|  | memset(to_viewer, 0, sizeof(to_viewer)); | 
|  | if (EVP_CipherUpdate(&init_ctx, to_viewer, &len, buf, n) == 0) { | 
|  | sslexit("securevnc_setup: EVP_CipherUpdate(init_ctx) failed"); | 
|  | exit(1); | 
|  | } | 
|  | to_viewer_len = len; | 
|  | } | 
|  | EVP_CIPHER_CTX_cleanup(&init_ctx); | 
|  | free(initkey); | 
|  |  | 
|  | /* print what we would send to the viewer (sent below): */ | 
|  | write(2, to_viewer, 12);	/* and first 12 bytes 'RFB ...' as message */ | 
|  |  | 
|  | /* now create the random session key: */ | 
|  | encrypted_keybuf = (unsigned char*) calloc(RSA_size(rsa), 1); | 
|  |  | 
|  | fprintf(stderr, "securevnc_setup: creating random session key: %d/%d\n", | 
|  | SECUREVNC_KEY_SIZE, SECUREVNC_RAND_KEY_SOURCE); | 
|  | keydata_len = SECUREVNC_RAND_KEY_SOURCE; | 
|  |  | 
|  | rc = RAND_bytes((unsigned char *)keydata, SECUREVNC_RAND_KEY_SOURCE); | 
|  | if (rc <= 0) { | 
|  | fprintf(stderr, "securevnc_setup: RAND_bytes() failed: %s\n", ERR_error_string(ERR_get_error(), NULL)); | 
|  | rc = RAND_pseudo_bytes((unsigned char *)keydata, SECUREVNC_RAND_KEY_SOURCE); | 
|  | fprintf(stderr, "securevnc_setup: RAND_pseudo_bytes() rc=%d\n", rc); | 
|  | if (getenv("RANDSTR")) { | 
|  | char *s = getenv("RANDSTR"); | 
|  | fprintf(stderr, "securevnc_setup: seeding with RANDSTR len=%d\n", strlen(s)); | 
|  | RAND_add(s, strlen(s), strlen(s)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* N.B. this will be repeated in enc_xfer() setup. */ | 
|  | EVP_BytesToKey(Cipher, Digest, NULL, (unsigned char *) keydata, keydata_len, 1, keystr, NULL); | 
|  |  | 
|  | /* encrypt the session key with the server's public rsa key: */ | 
|  | n = RSA_public_encrypt(SECUREVNC_KEY_SIZE, keystr, encrypted_keybuf, rsa, RSA_PKCS1_PADDING); | 
|  | if (n == -1) { | 
|  | sslexit("securevnc_setup: RSA_public_encrypt() failed"); | 
|  | exit(1); | 
|  | } | 
|  | fprintf(stderr, "securevnc_setup: encrypted session key size: %d. sending to server.\n", n); | 
|  |  | 
|  | /* send it to the server: */ | 
|  | write(server, encrypted_keybuf, n); | 
|  | free(encrypted_keybuf); | 
|  |  | 
|  | /* | 
|  | * Reply back with flags indicating cipher (same as one sent to | 
|  | * us) and we do not want client-side auth. | 
|  | * | 
|  | * We send it out on the wire in little endian order: | 
|  | */ | 
|  | if (securevnc_arc4) { | 
|  | write(server, (char *)&zero, 1); | 
|  | } else { | 
|  | write(server, (char *)&one, 1); | 
|  | } | 
|  | write(server, (char *)&zero, 1); | 
|  | write(server, (char *)&zero, 1); | 
|  | if (client_auth_req) { | 
|  | write(server, (char *)&sig, 1); | 
|  | } else { | 
|  | write(server, (char *)&zero, 1); | 
|  | } | 
|  |  | 
|  | if (client_auth_req && client_auth) { | 
|  | RSA *client_rsa = load_client_auth(client_auth); | 
|  | EVP_MD_CTX dctx; | 
|  | unsigned char digest[EVP_MAX_MD_SIZE], *signature; | 
|  | unsigned int ndig = 0, nsig = 0; | 
|  |  | 
|  | if (0) { | 
|  | /* for testing only, use the wrong RSA key: */ | 
|  | client_rsa = RSA_generate_key(2048, 0x10001, NULL, NULL); | 
|  | } | 
|  |  | 
|  | if (client_rsa == NULL) { | 
|  | fprintf(stderr, "securevnc_setup: problem reading rsa key from '%s'\n", client_auth); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | EVP_DigestInit(&dctx, EVP_sha1()); | 
|  | EVP_DigestUpdate(&dctx, keystr, SECUREVNC_KEY_SIZE); | 
|  | /* | 
|  | * Without something like the following MITM is still possible. | 
|  | * This is because the MITM knows keystr and can use it with | 
|  | * the server connection as well, and then he just forwards our | 
|  | * signed digest.  The additional information below would be the | 
|  | * MITM's rsa public key, and so the real VNC server will notice | 
|  | * the difference.  And MITM can't sign keystr+server_rsa.pub since | 
|  | * he doesn't have Viewer_ClientAuth.pkey. | 
|  | */ | 
|  | if (0) { | 
|  | EVP_DigestUpdate(&dctx, rsasav, SECUREVNC_RSA_PUBKEY_SIZE); | 
|  | if (!keystore_verified) { | 
|  | fprintf(stderr, "securevnc_setup:\n"); | 
|  | fprintf(stderr, "securevnc_setup: Warning: even *WITH* Client Authentication in SecureVNC,\n"); | 
|  | fprintf(stderr, "securevnc_setup: an attacker may be able to trick you into connecting to his\n"); | 
|  | fprintf(stderr, "securevnc_setup: fake VNC server and supplying VNC or Windows passwords, etc.\n"); | 
|  | fprintf(stderr, "securevnc_setup: To increase security manually verify the Server RSA key's MD5\n"); | 
|  | fprintf(stderr, "securevnc_setup: checksum and then have SSVNC save the key in its keystore to\n"); | 
|  | fprintf(stderr, "securevnc_setup: be used to verify the server in subsequent connections.\n"); | 
|  | fprintf(stderr, "securevnc_setup:\n"); | 
|  | } | 
|  | } else { | 
|  | if (!keystore_verified) { | 
|  | fprintf(stderr, "securevnc_setup:\n"); | 
|  | fprintf(stderr, "securevnc_setup: WARNING: THE FIRST VERSION OF THE SECUREVNC PROTOCOL IS\n"); | 
|  | fprintf(stderr, "securevnc_setup: WARNING: BEING USED.  *EVEN* WITH CLIENT AUTHENTICATION IT\n"); | 
|  | fprintf(stderr, "securevnc_setup: WARNING: IS SUSCEPTIBLE TO A MAN-IN-THE-MIDDLE ATTACK.\n"); | 
|  | fprintf(stderr, "securevnc_setup: To increase security manually verify the Server RSA key's MD5\n"); | 
|  | fprintf(stderr, "securevnc_setup: checksum and then have SSVNC save the key in its keystore to\n"); | 
|  | fprintf(stderr, "securevnc_setup: be used to verify the server in subsequent connections.\n"); | 
|  | fprintf(stderr, "securevnc_setup:\n"); | 
|  | } | 
|  | } | 
|  | EVP_DigestFinal(&dctx, (unsigned char *)digest, &ndig); | 
|  |  | 
|  | signature = (unsigned char *) calloc(RSA_size(client_rsa), 1); | 
|  | RSA_sign(NID_sha1, digest, ndig, signature, &nsig, client_rsa); | 
|  |  | 
|  | fprintf(stderr, "securevnc_setup: sending ClientAuth.pkey signed data: %d\n", nsig); | 
|  | write(server, signature, nsig); | 
|  | free(signature); | 
|  |  | 
|  | RSA_free(client_rsa); | 
|  | } | 
|  |  | 
|  | fprintf(stderr, "securevnc_setup: done.\n"); | 
|  |  | 
|  | /* now send the 'RFB ...' to the viewer */ | 
|  | if (to_viewer_len > 0) { | 
|  | write(viewer, to_viewer, to_viewer_len); | 
|  | } | 
|  | } | 
|  |  | 
|  | #ifndef ENC_DISABLE_SHOW_CERT | 
|  | static void enc_sslerrexit(void) { | 
|  | unsigned long err = ERR_get_error(); | 
|  |  | 
|  | if (err) { | 
|  | char str[256]; | 
|  | ERR_error_string(err, str); | 
|  | fprintf(stdout, "ssl error: %s\n", str); | 
|  | } | 
|  | exit(1); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static void show_cert(int sock) { | 
|  | #ifndef ENC_DISABLE_SHOW_CERT | 
|  | SSL_CTX *ctx; | 
|  | SSL *ssl = NULL; | 
|  | STACK_OF(X509) *sk = NULL; | 
|  | X509 *peer = NULL; | 
|  | SSL_CIPHER *c; | 
|  | BIO *bio; | 
|  | unsigned char *sid =  (unsigned char *) "ultravnc_dsm_helper SID"; | 
|  | long mode; | 
|  | int i; | 
|  |  | 
|  | fprintf(stdout, "CONNECTED(%08X)\n",sock); | 
|  |  | 
|  | SSL_library_init(); | 
|  | SSL_load_error_strings(); | 
|  |  | 
|  | if (!RAND_status()) { | 
|  | RAND_poll(); | 
|  | } | 
|  | /* this is not for a secured connection. */ | 
|  | for (i=0; i < 100; i++) { | 
|  | if (!RAND_status()) { | 
|  | char tmp[32]; | 
|  | sprintf(tmp, "%d", getpid() * (17 + i)); | 
|  | RAND_add(tmp, strlen(tmp), 5); | 
|  | } else { | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | ctx = SSL_CTX_new( SSLv23_client_method() ); | 
|  | if (ctx == NULL) { | 
|  | fprintf(stdout, "show_cert: SSL_CTX_new failed.\n"); | 
|  | close(sock); | 
|  | enc_sslerrexit(); | 
|  | } | 
|  |  | 
|  | mode = 0; | 
|  | mode |= SSL_MODE_ENABLE_PARTIAL_WRITE; | 
|  | mode |= SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER; | 
|  | SSL_CTX_set_mode(ctx, mode); | 
|  |  | 
|  | if (getenv("ULTRAVNC_DSM_HELPER_SHOWCERT_ADH")) { | 
|  | SSL_CTX_set_cipher_list(ctx, "ADH:@STRENGTH"); | 
|  | SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); | 
|  | } | 
|  |  | 
|  | ssl = SSL_new(ctx); | 
|  |  | 
|  | if (ssl == NULL) { | 
|  | fprintf(stdout, "show_cert: SSL_new failed.\n"); | 
|  | close(sock); | 
|  | enc_sslerrexit(); | 
|  | } | 
|  |  | 
|  | SSL_set_session_id_context(ssl, sid, strlen((char *)sid)); | 
|  |  | 
|  | if (! SSL_set_fd(ssl, sock)) { | 
|  | fprintf(stdout, "show_cert: SSL_set_fd failed.\n"); | 
|  | close(sock); | 
|  | enc_sslerrexit(); | 
|  | } | 
|  |  | 
|  | SSL_set_connect_state(ssl); | 
|  |  | 
|  | if (SSL_connect(ssl) <= 0) { | 
|  | unsigned long err = ERR_get_error(); | 
|  | fprintf(stdout, "show_cert: SSL_connect failed.\n"); | 
|  | if (err) { | 
|  | char str[256]; | 
|  | ERR_error_string(err, str); | 
|  | fprintf(stdout, "ssl error: %s\n", str); | 
|  | } | 
|  | } | 
|  |  | 
|  | SSL_get_verify_result(ssl); | 
|  |  | 
|  | sk = SSL_get_peer_cert_chain(ssl); | 
|  | if (sk != NULL) { | 
|  | fprintf(stdout, "---\nCertificate chain\n"); | 
|  | for (i=0; i < sk_X509_num(sk); i++) { | 
|  | char buf[2048]; | 
|  | X509_NAME_oneline(X509_get_subject_name(sk_X509_value(sk,i)), buf, sizeof buf); | 
|  | fprintf(stdout, "%2d s:%s\n", i, buf); | 
|  | X509_NAME_oneline(X509_get_issuer_name(sk_X509_value(sk,i)), buf, sizeof buf); | 
|  | fprintf(stdout, "   i:%s\n", buf); | 
|  | } | 
|  | } else { | 
|  | fprintf(stdout, "show_cert: SSL_get_peer_cert_chain failed.\n"); | 
|  | } | 
|  | fprintf(stdout, "---\n"); | 
|  | peer = SSL_get_peer_certificate(ssl); | 
|  | bio = BIO_new_fp(stdout, BIO_NOCLOSE); | 
|  | if (peer != NULL) { | 
|  | char buf[2048]; | 
|  | BIO_printf(bio,"Server certificate\n"); | 
|  | PEM_write_bio_X509(bio, peer); | 
|  |  | 
|  | X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof buf); | 
|  | BIO_printf(bio,"subject=%s\n",buf); | 
|  | X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof buf); | 
|  | BIO_printf(bio,"issuer=%s\n",buf); | 
|  | } else { | 
|  | fprintf(stdout, "show_cert: SSL_get_peer_certificate failed.\n"); | 
|  | } | 
|  |  | 
|  | c = SSL_get_current_cipher(ssl); | 
|  | BIO_printf(bio,"---\nNew, %s, Cipher is %s\n", SSL_CIPHER_get_version(c), SSL_CIPHER_get_name(c)); | 
|  |  | 
|  | if (peer != NULL) { | 
|  | EVP_PKEY *pktmp; | 
|  | pktmp = X509_get_pubkey(peer); | 
|  | BIO_printf(bio,"Server public key is %d bit\n", EVP_PKEY_bits(pktmp)); | 
|  | EVP_PKEY_free(pktmp); | 
|  | } | 
|  | BIO_printf(bio,"---\nDONE\n---\n"); | 
|  |  | 
|  | fflush(stdout); | 
|  |  | 
|  | #endif | 
|  | close(sock); | 
|  | exit(0); | 
|  | } | 
|  |  | 
|  | #ifndef SOL_IPV6 | 
|  | #ifdef  IPPROTO_IPV6 | 
|  | #define SOL_IPV6 IPPROTO_IPV6 | 
|  | #endif | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | * Listens on incoming port for a client, then connects to remote server. | 
|  | * Then forks into two processes one is the encrypter the other the | 
|  | * decrypter. | 
|  | */ | 
|  | static void enc_connections(int listen_port, char *connect_host, int connect_port) { | 
|  | int listen_fd = -1, listen_fd6 = -1, conn1 = -1, conn2 = -1, ret, one = 1; | 
|  | socklen_t clen; | 
|  | struct hostent *hp; | 
|  | struct sockaddr_in client, server; | 
|  | fd_set fds; | 
|  | int maxfd = -1; | 
|  |  | 
|  | /* zero means use stdio (preferably from socketpair()) */ | 
|  | if (listen_port == 0) { | 
|  | conn1 = fileno(stdin); | 
|  | goto use_stdio; | 
|  | } | 
|  |  | 
|  | if (!strcmp(cipher, "showcert")) { | 
|  | goto use_stdio; | 
|  | } | 
|  |  | 
|  | /* fd=n,m means use the supplied already established sockets */ | 
|  | if (sscanf(connect_host, "fd=%d,%d", &conn1, &conn2) == 2) { | 
|  | goto use_input_fds; | 
|  | } | 
|  |  | 
|  | /* create the listening socket: */ | 
|  | memset(&client, 0, sizeof(client)); | 
|  | client.sin_family = AF_INET; | 
|  | if (listen_port < 0) { | 
|  | /* negative port means use loopback */ | 
|  | client.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | 
|  | client.sin_port = htons(-listen_port); | 
|  | } else { | 
|  | client.sin_addr.s_addr = htonl(INADDR_ANY); | 
|  | client.sin_port = htons(listen_port); | 
|  | } | 
|  |  | 
|  | listen_fd = socket(AF_INET, SOCK_STREAM, 0); | 
|  | if (listen_fd < 0) { | 
|  | perror("socket"); | 
|  | goto try6; | 
|  | } | 
|  |  | 
|  | ret = setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, | 
|  | (char *)&one, sizeof(one)); | 
|  | if (ret < 0) { | 
|  | perror("setsockopt"); | 
|  | close(listen_fd); | 
|  | listen_fd = -1; | 
|  | goto try6; | 
|  | } | 
|  |  | 
|  | ret = bind(listen_fd, (struct sockaddr *) &client, sizeof(client)); | 
|  | if (ret < 0) { | 
|  | perror("bind"); | 
|  | close(listen_fd); | 
|  | listen_fd = -1; | 
|  | goto try6; | 
|  | } | 
|  |  | 
|  | ret = listen(listen_fd, 2); | 
|  | if (ret < 0) { | 
|  | perror("listen"); | 
|  | close(listen_fd); | 
|  | listen_fd = -1; | 
|  | goto try6; | 
|  | } | 
|  |  | 
|  | try6: | 
|  | #ifdef AF_INET6 | 
|  | if (!getenv("ULTRAVNC_DSM_HELPER_NOIPV6")) { | 
|  | struct sockaddr_in6 sin; | 
|  | int one = 1, sock = -1; | 
|  |  | 
|  | sock = socket(AF_INET6, SOCK_STREAM, 0); | 
|  | if (sock < 0) { | 
|  | perror("socket6"); | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)) < 0) { | 
|  | perror("setsockopt6 SO_REUSEADDR"); | 
|  | close(sock); | 
|  | sock = -1; | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | #if defined(SOL_IPV6) && defined(IPV6_V6ONLY) | 
|  | if (setsockopt(sock, SOL_IPV6, IPV6_V6ONLY, (char *)&one, sizeof(one)) < 0) { | 
|  | perror("setsockopt6 IPV6_V6ONLY"); | 
|  | close(sock); | 
|  | sock = -1; | 
|  | goto fail; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | memset((char *)&sin, 0, sizeof(sin)); | 
|  | sin.sin6_family = AF_INET6; | 
|  |  | 
|  | if (listen_port < 0) { | 
|  | sin.sin6_addr = in6addr_loopback; | 
|  | sin.sin6_port = htons(-listen_port); | 
|  | } else { | 
|  | sin.sin6_addr = in6addr_any; | 
|  | sin.sin6_port = htons(listen_port); | 
|  | } | 
|  |  | 
|  | if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) { | 
|  | perror("bind6"); | 
|  | close(sock); | 
|  | sock = -1; | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | if (listen(sock, 2) < 0) { | 
|  | perror("listen6"); | 
|  | close(sock); | 
|  | sock = -1; | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | fail: | 
|  | listen_fd6 = sock; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | if (listen_fd < 0 && listen_fd6 < 0) { | 
|  | fprintf(stderr, "%s: could not listen on port: %d\n", | 
|  | prog, listen_port); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | fprintf(stderr, "%s: waiting for connection on port: %d\n", | 
|  | prog, listen_port); | 
|  |  | 
|  | /* wait for a connection: */ | 
|  | FD_ZERO(&fds); | 
|  | if (listen_fd >= 0) { | 
|  | FD_SET(listen_fd, &fds); | 
|  | if (listen_fd > maxfd) { | 
|  | maxfd = listen_fd; | 
|  | } | 
|  | } | 
|  | if (listen_fd6 >= 0) { | 
|  | FD_SET(listen_fd6, &fds); | 
|  | if (listen_fd6 > maxfd) { | 
|  | maxfd = listen_fd6; | 
|  | } | 
|  | } | 
|  | if (select(maxfd+1, &fds, NULL, NULL, NULL) <= 0) { | 
|  | perror("select"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | if (FD_ISSET(listen_fd, &fds)) { | 
|  | clen = sizeof(client); | 
|  | conn1 = accept(listen_fd, (struct sockaddr *) &client, &clen); | 
|  | if (conn1 < 0) { | 
|  | perror("accept"); | 
|  | exit(1); | 
|  | } | 
|  | } else if (FD_ISSET(listen_fd6, &fds)) { | 
|  | #ifdef AF_INET6 | 
|  | struct sockaddr_in6 addr; | 
|  | socklen_t addrlen = sizeof(addr); | 
|  |  | 
|  | conn1 = accept(listen_fd6, (struct sockaddr *) &addr, &addrlen); | 
|  | if (conn1 < 0) { | 
|  | perror("accept6"); | 
|  | exit(1); | 
|  | } | 
|  | #else | 
|  | fprintf(stderr, "No IPv6 / AF_INET6 support.\n"); | 
|  | exit(1); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | if (setsockopt(conn1, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)) < 0) { | 
|  | perror("setsockopt TCP_NODELAY"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | /* done with the listening socket(s): */ | 
|  | if (listen_fd >= 0) { | 
|  | close(listen_fd); | 
|  | } | 
|  | if (listen_fd6 >= 0) { | 
|  | close(listen_fd6); | 
|  | } | 
|  |  | 
|  | if (getenv("ULTRAVNC_DSM_HELPER_BG")) { | 
|  | int p, n; | 
|  | if ((p = fork()) > 0)  { | 
|  | fprintf(stderr, "%s: putting child %d in background.\n", | 
|  | prog, p); | 
|  | exit(0); | 
|  | } else if (p == -1) { | 
|  | fprintf(stderr, "%s: could not fork\n", prog); | 
|  | perror("fork"); | 
|  | exit(1); | 
|  | } | 
|  | if (setsid() == -1) { | 
|  | fprintf(stderr, "%s: setsid failed\n", prog); | 
|  | perror("setsid"); | 
|  | exit(1); | 
|  | } | 
|  | /* adjust our stdio */ | 
|  | n = open("/dev/null", O_RDONLY); | 
|  | dup2(n, 0); | 
|  | dup2(n, 1); | 
|  | dup2(n, 2); | 
|  | if (n > 2) { | 
|  | close(n); | 
|  | } | 
|  | } | 
|  |  | 
|  | use_stdio: | 
|  |  | 
|  | fprintf(stderr, "%s: got connection: %d\n", prog, conn1); | 
|  |  | 
|  | /* now connect to remote server: */ | 
|  | memset(&server, 0, sizeof(server)); | 
|  | server.sin_family = AF_INET; | 
|  | server.sin_port = htons(connect_port); | 
|  |  | 
|  | if ((server.sin_addr.s_addr = inet_addr(connect_host)) == htonl(INADDR_NONE)) { | 
|  | if (!(hp = gethostbyname(connect_host))) { | 
|  | perror("gethostbyname"); | 
|  | goto tryconn6; | 
|  | } | 
|  | server.sin_addr.s_addr = *(unsigned long *)hp->h_addr; | 
|  | } | 
|  |  | 
|  | conn2 = socket(AF_INET, SOCK_STREAM, 0); | 
|  | if (conn2 < 0) { | 
|  | perror("socket"); | 
|  | goto tryconn6; | 
|  | } | 
|  |  | 
|  | if (connect(conn2, (struct sockaddr *)&server, (sizeof(server))) < 0) { | 
|  | perror("connect"); | 
|  | goto tryconn6; | 
|  | } | 
|  |  | 
|  | tryconn6: | 
|  | #ifdef AF_INET6 | 
|  | if (conn2 < 0 && !getenv("ULTRAVNC_DSM_HELPER_NOIPV6")) { | 
|  | int err; | 
|  | struct addrinfo *ai; | 
|  | struct addrinfo hints; | 
|  | char service[32]; | 
|  |  | 
|  | fprintf(stderr, "connect[ipv6]: trying to connect via IPv6 to %s\n", connect_host); | 
|  | conn2 = -1; | 
|  |  | 
|  | memset(&hints, 0, sizeof(hints)); | 
|  | sprintf(service, "%d", connect_port); | 
|  |  | 
|  | hints.ai_family = AF_UNSPEC; | 
|  | hints.ai_socktype = SOCK_STREAM; | 
|  | #ifdef AI_ADDRCONFIG | 
|  | hints.ai_flags |= AI_ADDRCONFIG; | 
|  | #endif | 
|  | #ifdef AI_NUMERICSERV | 
|  | hints.ai_flags |= AI_NUMERICSERV; | 
|  | #endif | 
|  |  | 
|  | err = getaddrinfo(connect_host, service, &hints, &ai); | 
|  | if (err != 0) { | 
|  | fprintf(stderr, "getaddrinfo[%d]: %s\n", err, gai_strerror(err)); | 
|  | } else { | 
|  | struct addrinfo *ap = ai; | 
|  | while (ap != NULL) { | 
|  | int fd = -1; | 
|  | fd = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol); | 
|  | if (fd == -1) { | 
|  | perror("socket6"); | 
|  | } else { | 
|  | int dmsg = 0; | 
|  | int res = connect(fd, ap->ai_addr, ap->ai_addrlen); | 
|  | #if defined(SOL_IPV6) && defined(IPV6_V6ONLY) | 
|  | if (res != 0) { | 
|  | int zero = 0; | 
|  | perror("connect6"); | 
|  | dmsg = 1; | 
|  | if (setsockopt(fd, SOL_IPV6, IPV6_V6ONLY, (char *)&zero, sizeof(zero)) == 0) { | 
|  | fprintf(stderr, "connect[ipv6]: trying again with IPV6_V6ONLY=0\n"); | 
|  | res = connect(fd, ap->ai_addr, ap->ai_addrlen); | 
|  | dmsg = 0; | 
|  | } | 
|  | } | 
|  | #endif | 
|  | if (res == 0) { | 
|  | conn2 = fd; | 
|  | break; | 
|  | } else { | 
|  | if (!dmsg) perror("connect6"); | 
|  | close(fd); | 
|  | } | 
|  | } | 
|  | ap = ap->ai_next; | 
|  | } | 
|  | freeaddrinfo(ai); | 
|  | } | 
|  | } | 
|  | #endif | 
|  | if (conn2 < 0) { | 
|  | fprintf(stderr, "could not connect to %s\n", connect_host); | 
|  | exit(1); | 
|  | } | 
|  | if (conn2 >= 0 && setsockopt(conn2, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)) < 0) { | 
|  | perror("setsockopt TCP_NODELAY"); | 
|  | } | 
|  |  | 
|  | use_input_fds: | 
|  |  | 
|  | if (!strcmp(cipher, "showcert")) { | 
|  | show_cert(conn2); | 
|  | close(conn2); | 
|  | exit(0); | 
|  | } | 
|  |  | 
|  | if (securevnc) { | 
|  | securevnc_setup(conn1, conn2); | 
|  | } | 
|  |  | 
|  | /* fork into two processes; one for each direction: */ | 
|  | parent = getpid(); | 
|  |  | 
|  | child = fork(); | 
|  |  | 
|  | if (child == (pid_t) -1) { | 
|  | /* couldn't fork... */ | 
|  | perror("fork"); | 
|  | close(conn1); | 
|  | close(conn2); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | /* Do transfer/encode/decode loop: */ | 
|  |  | 
|  | if (child == 0) { | 
|  | /* encrypter: local-viewer -> remote-server */ | 
|  | if (!strcmp(cipher, "none") || !strcmp(cipher, "relay")) { | 
|  | enc_raw_xfer(conn1, conn2); | 
|  | } else { | 
|  | enc_xfer(conn1, conn2, 1); | 
|  | } | 
|  | } else { | 
|  | /* decrypter: remote-server -> local-viewer */ | 
|  | if (!strcmp(cipher, "none") || !strcmp(cipher, "relay")) { | 
|  | enc_raw_xfer(conn2, conn1); | 
|  | } else { | 
|  | enc_xfer(conn2, conn1, 0); | 
|  | } | 
|  | } | 
|  | } | 
|  | #endif /* ENC_HAVE_OPENSSL */ | 
|  |  | 
|  | static void doloop (int argc, char *argv[]) { | 
|  | int ms = atoi(getenv("ULTRAVNC_DSM_HELPER_LOOP")); | 
|  | if (ms > 0) { | 
|  | char *cmd; | 
|  | int i, len = 0; | 
|  | for (i = 0; i < argc; i++) { | 
|  | len += strlen(argv[i]) + 2; | 
|  | } | 
|  | cmd = (char *)malloc(len); | 
|  | cmd[0] = '\0'; | 
|  | for (i = 0; i < argc; i++) { | 
|  | strcat(cmd, argv[i]); | 
|  | if (i < argc - 1) { | 
|  | strcat(cmd, " "); | 
|  | } | 
|  | } | 
|  |  | 
|  | putenv("ULTRAVNC_DSM_HELPER_LOOP_SET=1"); | 
|  | if (ms == 1) { | 
|  | ms = 500; | 
|  | } | 
|  | i = 0; | 
|  | while (1) { | 
|  | fprintf(stderr, "loop running[%d]: %s\n", ++i, cmd); | 
|  | system(cmd); | 
|  | usleep(1000 * ms); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | extern int main (int argc, char *argv[]) { | 
|  | char *kf, *q; | 
|  |  | 
|  | if (getenv("ULTRAVNC_DSM_HELPER_LOOP")) { | 
|  | if (!getenv("ULTRAVNC_DSM_HELPER_LOOP_SET")) { | 
|  | doloop(argc, argv); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (argc == 3) { | 
|  | if (!strcmp(argv[1], "showcert")) { | 
|  | enc_do(argv[1], NULL, NULL, argv[2]); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | if (argc == 4) { | 
|  | if (!strcmp(argv[1], "none") || !strcmp(argv[1], "relay")) { | 
|  | enc_do(argv[1], NULL, argv[2], argv[3]); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | if (argc < 5) { | 
|  | fprintf(stdout, "%s\n", usage); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | /* guard against pw= on cmdline (e.g. linux) */ | 
|  | kf = strdup(argv[2]); | 
|  | q = strstr(argv[2], "pw="); | 
|  | if (q) { | 
|  | while (*q != '\0') { | 
|  | *q = '\0';	/* now ps(1) won't show it */ | 
|  | q++; | 
|  | } | 
|  | } | 
|  |  | 
|  | enc_do(argv[1], kf, argv[3], argv[4]); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * a crude utility to have this work "keyless" i.e. the vnc password | 
|  | * is used instead of a pre-shared key file. | 
|  | */ | 
|  |  | 
|  | /* | 
|  |  | 
|  | #!/usr/bin/perl | 
|  | # | 
|  | # md5_to_rc4key.pl | 
|  | # | 
|  | # This program requires md5sum(1) installed on your machine. | 
|  | # | 
|  | # It translates a VNC password to a ultravnc dsm plugin | 
|  | # compatible key file. | 
|  | # | 
|  | # Supply VNC password on cmdline, capture in key file: | 
|  | # | 
|  | #	md5_to_rc4key.pl swordfish    > rc4.key | 
|  | #	md5_to_rc4key.pl -a swordfish > arc4.key | 
|  | # | 
|  | # Use rc4.key with ultravnc_dsm_helper in msrc4 mode, | 
|  | # or arc4.key in either arc4 or aesv4 mode. | 
|  | # | 
|  | # | 
|  | $rfmt = 1; | 
|  | if ($ARGV[0] eq '-a') { | 
|  | $rfmt = 0; | 
|  | shift; | 
|  | } | 
|  |  | 
|  | # n.b. this is not super secure against bad locals... | 
|  |  | 
|  | $pw = shift; | 
|  | $tmp = "/tmp/md5out.$$"; | 
|  |  | 
|  | open(MD5, "| md5sum > $tmp"); | 
|  | print MD5 $pw; | 
|  | close MD5; | 
|  |  | 
|  | $md5 = `cat $tmp`; | 
|  | unlink $tmp; | 
|  |  | 
|  | ($md5, $junk) = split(/\s/, $md5); | 
|  |  | 
|  | print "128 bit" if $rfmt; | 
|  | print 'a' x 4	if $rfmt; | 
|  | print 'b' x 12	if $rfmt; | 
|  |  | 
|  | $str = ''; | 
|  | foreach $d (split(//, $md5)) { | 
|  | $str .= $d; | 
|  | if (length($str) == 2) { | 
|  | push @key, $str; | 
|  | $str = ''; | 
|  | } | 
|  | } | 
|  |  | 
|  | @key = (reverse @key) if $rfmt; | 
|  |  | 
|  | foreach $h (@key) { | 
|  | $c = pack('c', hex("0x$h")); | 
|  | print $c; | 
|  | } | 
|  |  | 
|  | print 'c' x 48	if $rfmt; | 
|  |  | 
|  | */ | 
|  | #endif /* _X11VNC_ENC_H */ |