|
|
@ -36,7 +36,7 @@ so, delete this exception statement from your version.
|
|
|
|
/* -- enc.h -- */
|
|
|
|
/* -- enc.h -- */
|
|
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
#if 0
|
|
|
|
:r /home/runge/ultraSC/rc4/ultravnc_dsm_helper.c
|
|
|
|
:r /home/runge/uvnc/ultraSC/rc4/ultravnc_dsm_helper.c
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
@ -100,14 +100,29 @@ static char *usage =
|
|
|
|
"\n"
|
|
|
|
"\n"
|
|
|
|
"usage: ultravnc_dsm_helper cipher keyfile listenport remotehost:port\n"
|
|
|
|
"usage: ultravnc_dsm_helper cipher keyfile listenport remotehost:port\n"
|
|
|
|
"\n"
|
|
|
|
"\n"
|
|
|
|
"e.g.: ultravnc_dsm_helper arc4 ./arc4.key 5901 snoopy.com:5900\n"
|
|
|
|
"e.g.: ultravnc_dsm_helper arc4 ./arc4.key 5901 snoopy.net:5900\n"
|
|
|
|
"\n"
|
|
|
|
"\n"
|
|
|
|
" cipher: specify 'msrc4', 'msrc4_sc', 'arc4', 'aesv2',\n"
|
|
|
|
" cipher: specify 'msrc4', 'msrc4_sc', 'arc4', 'aesv2',\n"
|
|
|
|
" 'aes-cfb', 'aes256', 'blowfish', or '3des'.\n"
|
|
|
|
" 'aes-cfb', 'aes256', 'blowfish', '3des',\n"
|
|
|
|
|
|
|
|
" 'securevnc'.\n"
|
|
|
|
"\n"
|
|
|
|
"\n"
|
|
|
|
" 'msrc4_sc' enables a workaround for UVNC SC -plugin use.\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"
|
|
|
|
"\n"
|
|
|
|
" use '.' to have it try to guess the cipher from the keyfile name.\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"
|
|
|
|
"\n"
|
|
|
|
" use 'rev:arc4', etc. to reverse the roles of encrypter and decrypter.\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"
|
|
|
|
" (i.e. if you want to use it for a vnc server, not vnc viewer)\n"
|
|
|
@ -119,8 +134,9 @@ static char *usage =
|
|
|
|
" use 'noultra:rev:...' if both are to be supplied.\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"
|
|
|
|
" 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"
|
|
|
|
" 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"
|
|
|
|
" 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"
|
|
|
|
" listenport: port to listen for incoming connection on. (use 0 to connect\n"
|
|
|
|
" to stdio, use a negative value to force localhost)\n"
|
|
|
|
" to stdio, use a negative value to force localhost)\n"
|
|
|
@ -182,6 +198,8 @@ static char *prog = "ultravnc_dsm_helper";
|
|
|
|
#if ENC_HAVE_OPENSSL
|
|
|
|
#if ENC_HAVE_OPENSSL
|
|
|
|
#include <openssl/evp.h>
|
|
|
|
#include <openssl/evp.h>
|
|
|
|
#include <openssl/rand.h>
|
|
|
|
#include <openssl/rand.h>
|
|
|
|
|
|
|
|
#include <openssl/rsa.h>
|
|
|
|
|
|
|
|
#include <openssl/err.h>
|
|
|
|
static const EVP_CIPHER *Cipher;
|
|
|
|
static const EVP_CIPHER *Cipher;
|
|
|
|
static const EVP_MD *Digest;
|
|
|
|
static const EVP_MD *Digest;
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
@ -229,6 +247,18 @@ static pid_t parent, child;
|
|
|
|
# define PRINT_LOOP_DBG3
|
|
|
|
# define PRINT_LOOP_DBG3
|
|
|
|
#endif
|
|
|
|
#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);
|
|
|
|
static void enc_connections(int, char*, int);
|
|
|
|
|
|
|
|
|
|
|
|
#if !ENC_HAVE_OPENSSL
|
|
|
|
#if !ENC_HAVE_OPENSSL
|
|
|
@ -261,7 +291,7 @@ extern void enc_do(char *ciph, char *keyfile, char *lport, char *rhp) {
|
|
|
|
struct stat sb;
|
|
|
|
struct stat sb;
|
|
|
|
char *q, *p, *connect_host;
|
|
|
|
char *q, *p, *connect_host;
|
|
|
|
char tmp[16];
|
|
|
|
char tmp[16];
|
|
|
|
int fd, len, listen_port, connect_port, mbits;
|
|
|
|
int fd, len = 0, listen_port, connect_port, mbits;
|
|
|
|
|
|
|
|
|
|
|
|
q = ciph;
|
|
|
|
q = ciph;
|
|
|
|
|
|
|
|
|
|
|
@ -303,6 +333,10 @@ extern void enc_do(char *ciph, char *keyfile, char *lport, char *rhp) {
|
|
|
|
} else if (strstr(q, "3des") == q) {
|
|
|
|
} else if (strstr(q, "3des") == q) {
|
|
|
|
Cipher = EVP_des_ede3_cfb(); cipher = "3des";
|
|
|
|
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, ".") == q) {
|
|
|
|
} else if (strstr(q, ".") == q) {
|
|
|
|
/* otherwise, try to guess cipher from key filename: */
|
|
|
|
/* otherwise, try to guess cipher from key filename: */
|
|
|
|
if (strstr(keyfile, "arc4.key")) {
|
|
|
|
if (strstr(keyfile, "arc4.key")) {
|
|
|
@ -326,6 +360,10 @@ extern void enc_do(char *ciph, char *keyfile, char *lport, char *rhp) {
|
|
|
|
} else if (strstr(keyfile, "3des.key")) {
|
|
|
|
} else if (strstr(keyfile, "3des.key")) {
|
|
|
|
Cipher = EVP_des_ede3_cfb(); cipher = "3des";
|
|
|
|
Cipher = EVP_des_ede3_cfb(); cipher = "3des";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else if (strstr(keyfile, "securevnc.")) {
|
|
|
|
|
|
|
|
Cipher = EVP_aes_128_ofb(); cipher = "securevnc";
|
|
|
|
|
|
|
|
securevnc = 1;
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "cannot figure out cipher, supply 'msrc4', 'arc4', or 'aesv2' ...\n");
|
|
|
|
fprintf(stderr, "cannot figure out cipher, supply 'msrc4', 'arc4', or 'aesv2' ...\n");
|
|
|
|
exit(1);
|
|
|
|
exit(1);
|
|
|
@ -336,7 +374,11 @@ extern void enc_do(char *ciph, char *keyfile, char *lport, char *rhp) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* set the default message digest (md5) */
|
|
|
|
/* set the default message digest (md5) */
|
|
|
|
Digest = EVP_md5();
|
|
|
|
if (!securevnc) {
|
|
|
|
|
|
|
|
Digest = EVP_md5();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
Digest = EVP_sha1();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Look for user specified salt and IV sizes at the end
|
|
|
|
* Look for user specified salt and IV sizes at the end
|
|
|
@ -406,6 +448,15 @@ extern void enc_do(char *ciph, char *keyfile, char *lport, char *rhp) {
|
|
|
|
|
|
|
|
|
|
|
|
/* check for and read in the key file */
|
|
|
|
/* check for and read in the key file */
|
|
|
|
memset(keydata, 0, sizeof(keydata));
|
|
|
|
memset(keydata, 0, sizeof(keydata));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 (stat(keyfile, &sb) != 0) {
|
|
|
|
if (strstr(keyfile, "pw=") == keyfile) {
|
|
|
|
if (strstr(keyfile, "pw=") == keyfile) {
|
|
|
|
/* user specified key/password on cmdline */
|
|
|
|
/* user specified key/password on cmdline */
|
|
|
@ -498,12 +549,13 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
|
|
|
|
unsigned char E_keystr[EVP_MAX_KEY_LENGTH];
|
|
|
|
unsigned char E_keystr[EVP_MAX_KEY_LENGTH];
|
|
|
|
unsigned char D_keystr[EVP_MAX_KEY_LENGTH];
|
|
|
|
unsigned char D_keystr[EVP_MAX_KEY_LENGTH];
|
|
|
|
EVP_CIPHER_CTX E_ctx, D_ctx;
|
|
|
|
EVP_CIPHER_CTX E_ctx, D_ctx;
|
|
|
|
EVP_CIPHER_CTX *ctx;
|
|
|
|
EVP_CIPHER_CTX *ctx = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
unsigned char buf[BSIZE], out[BSIZE];
|
|
|
|
unsigned char buf[BSIZE], out[BSIZE];
|
|
|
|
unsigned char *psrc = NULL, *keystr;
|
|
|
|
unsigned char *psrc = NULL, *keystr;
|
|
|
|
unsigned char salt[SALT+1];
|
|
|
|
unsigned char salt[SALT+1];
|
|
|
|
unsigned char ivec[EVP_MAX_IV_LENGTH];
|
|
|
|
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 i, cnt, len, m, n = 0, vb = 0, first = 1;
|
|
|
|
int whoops = 1; /* for the msrc4 problem */
|
|
|
|
int whoops = 1; /* for the msrc4 problem */
|
|
|
@ -513,7 +565,7 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
|
|
|
|
memset(buf, 0, BSIZE);
|
|
|
|
memset(buf, 0, BSIZE);
|
|
|
|
memset(out, 0, BSIZE);
|
|
|
|
memset(out, 0, BSIZE);
|
|
|
|
memset(salt, 0, sizeof(salt));
|
|
|
|
memset(salt, 0, sizeof(salt));
|
|
|
|
memset(ivec, 0, sizeof(ivec));
|
|
|
|
memset(ivec_real, 0, sizeof(ivec_real));
|
|
|
|
memset(E_keystr, 0, sizeof(E_keystr));
|
|
|
|
memset(E_keystr, 0, sizeof(E_keystr));
|
|
|
|
memset(D_keystr, 0, sizeof(D_keystr));
|
|
|
|
memset(D_keystr, 0, sizeof(D_keystr));
|
|
|
|
|
|
|
|
|
|
|
@ -538,7 +590,22 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
|
|
|
|
encstr = encrypt ? "encrypt" : "decrypt"; /* string for messages */
|
|
|
|
encstr = encrypt ? "encrypt" : "decrypt"; /* string for messages */
|
|
|
|
encsym = encrypt ? "+" : "-";
|
|
|
|
encsym = encrypt ? "+" : "-";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* use the encryption/decryption context variables below */
|
|
|
|
if (encrypt) {
|
|
|
|
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 */
|
|
|
|
/* encrypter initializes the salt and initialization vector */
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
@ -558,10 +625,6 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
|
|
|
|
|
|
|
|
|
|
|
|
ENC_PT_DBG(buf, n);
|
|
|
|
ENC_PT_DBG(buf, n);
|
|
|
|
|
|
|
|
|
|
|
|
/* use the encryption context variables below */
|
|
|
|
|
|
|
|
ctx = &E_ctx;
|
|
|
|
|
|
|
|
keystr = E_keystr;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
/* decrypter needs to read salt + iv from the wire: */
|
|
|
|
/* decrypter needs to read salt + iv from the wire: */
|
|
|
|
|
|
|
|
|
|
|
@ -615,10 +678,6 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* use the decryption context variables below */
|
|
|
|
|
|
|
|
ctx = &D_ctx;
|
|
|
|
|
|
|
|
keystr = D_keystr;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* debug output */
|
|
|
|
/* debug output */
|
|
|
@ -644,8 +703,10 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
|
|
|
|
(unsigned char *) keydata, NULL, encrypt);
|
|
|
|
(unsigned char *) keydata, NULL, encrypt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
/* XXX might not be correct */
|
|
|
|
/* XXX might not be correct, just exit. */
|
|
|
|
|
|
|
|
fprintf(stderr, "%s: %s - Not sure about msrc4 && !whoops case, exiting.\n", prog, encstr);
|
|
|
|
exit(1);
|
|
|
|
exit(1);
|
|
|
|
|
|
|
|
|
|
|
|
EVP_BytesToKey(Cipher, Digest, NULL, (unsigned char *) keydata,
|
|
|
|
EVP_BytesToKey(Cipher, Digest, NULL, (unsigned char *) keydata,
|
|
|
|
keydata_len, 1, keystr, ivec);
|
|
|
|
keydata_len, 1, keystr, ivec);
|
|
|
|
EVP_CIPHER_CTX_init(ctx);
|
|
|
|
EVP_CIPHER_CTX_init(ctx);
|
|
|
@ -654,10 +715,12 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
unsigned char *in_salt;
|
|
|
|
unsigned char *in_salt = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
/* check salt and IV source and size. */
|
|
|
|
/* check salt and IV source and size. */
|
|
|
|
if (salt_size <= 0) {
|
|
|
|
if (securevnc) {
|
|
|
|
|
|
|
|
in_salt = NULL;
|
|
|
|
|
|
|
|
} else if (salt_size <= 0) {
|
|
|
|
/* let salt_size = 0 mean keep it out of the MD5 */
|
|
|
|
/* let salt_size = 0 mean keep it out of the MD5 */
|
|
|
|
fprintf(stderr, "%s: %s - WARNING: no salt\n",
|
|
|
|
fprintf(stderr, "%s: %s - WARNING: no salt\n",
|
|
|
|
prog, encstr);
|
|
|
|
prog, encstr);
|
|
|
@ -665,7 +728,8 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
in_salt = salt;
|
|
|
|
in_salt = salt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ivec_size < Cipher->iv_len) {
|
|
|
|
|
|
|
|
|
|
|
|
if (ivec_size < Cipher->iv_len && !securevnc) {
|
|
|
|
fprintf(stderr, "%s: %s - WARNING: short IV %d < %d\n",
|
|
|
|
fprintf(stderr, "%s: %s - WARNING: short IV %d < %d\n",
|
|
|
|
prog, encstr, ivec_size, Cipher->iv_len);
|
|
|
|
prog, encstr, ivec_size, Cipher->iv_len);
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -697,6 +761,9 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
|
|
|
|
* Ultra DSM compatibility mode. Note that this
|
|
|
|
* Ultra DSM compatibility mode. Note that this
|
|
|
|
* clobbers the ivec we set up above! Under
|
|
|
|
* clobbers the ivec we set up above! Under
|
|
|
|
* noultra we overwrite ivec only if ivec_size=0.
|
|
|
|
* 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,
|
|
|
|
EVP_BytesToKey(Cipher, Digest, in_salt, (unsigned char *) keydata,
|
|
|
|
keydata_len, 1, keystr, ivec);
|
|
|
|
keydata_len, 1, keystr, ivec);
|
|
|
@ -710,13 +777,21 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
|
|
|
|
/* set the cipher & initialize */
|
|
|
|
/* set the cipher & initialize */
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* XXX N.B.: DSM plugin had encrypt=1 for both
|
|
|
|
* XXX N.B.: DSM plugin implementation had encrypt=1
|
|
|
|
* (i.e. perfectly symmetric)
|
|
|
|
* for both (i.e. perfectly symmetric)
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
EVP_CipherInit_ex(ctx, Cipher, NULL, keystr, ivec, encrypt);
|
|
|
|
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 */
|
|
|
|
/* debug output */
|
|
|
|
PRINT_KEYSTR_AND_FRIENDS;
|
|
|
|
PRINT_KEYSTR_AND_FRIENDS;
|
|
|
|
|
|
|
|
|
|
|
@ -825,6 +900,474 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Listens on incoming port for a client, then connects to remote server.
|
|
|
|
* Listens on incoming port for a client, then connects to remote server.
|
|
|
|
* Then forks into two processes one is the encrypter the other the
|
|
|
|
* Then forks into two processes one is the encrypter the other the
|
|
|
@ -931,6 +1474,10 @@ static void enc_connections(int listen_port, char *connect_host, int connect_por
|
|
|
|
|
|
|
|
|
|
|
|
use_input_fds:
|
|
|
|
use_input_fds:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (securevnc) {
|
|
|
|
|
|
|
|
securevnc_setup(conn1, conn2);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* fork into two processes; one for each direction: */
|
|
|
|
/* fork into two processes; one for each direction: */
|
|
|
|
parent = getpid();
|
|
|
|
parent = getpid();
|
|
|
|
|
|
|
|
|
|
|
@ -960,7 +1507,7 @@ extern int main (int argc, char *argv[]) {
|
|
|
|
char *kf, *q;
|
|
|
|
char *kf, *q;
|
|
|
|
|
|
|
|
|
|
|
|
if (argc < 4) {
|
|
|
|
if (argc < 4) {
|
|
|
|
fprintf(stderr, "%s\n", usage);
|
|
|
|
fprintf(stdout, "%s\n", usage);
|
|
|
|
exit(1);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|