diff options
Diffstat (limited to 'x11vnc/enc.h')
-rw-r--r-- | x11vnc/enc.h | 2165 |
1 files changed, 0 insertions, 2165 deletions
diff --git a/x11vnc/enc.h b/x11vnc/enc.h deleted file mode 100644 index 55d49bb..0000000 --- a/x11vnc/enc.h +++ /dev/null @@ -1,2165 +0,0 @@ -/* - Copyright (C) 2002-2010 Karl J. Runge <runge@karlrunge.com> - 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 <runge@karlrunge.com> - * 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 */ |