summaryrefslogtreecommitdiffstats
path: root/x11vnc/enc.h
diff options
context:
space:
mode:
Diffstat (limited to 'x11vnc/enc.h')
-rw-r--r--x11vnc/enc.h599
1 files changed, 573 insertions, 26 deletions
diff --git a/x11vnc/enc.h b/x11vnc/enc.h
index 26e3c01..4c43b8d 100644
--- a/x11vnc/enc.h
+++ b/x11vnc/enc.h
@@ -36,7 +36,7 @@ so, delete this exception statement from your version.
/* -- enc.h -- */
#if 0
-:r /home/runge/ultraSC/rc4/ultravnc_dsm_helper.c
+:r /home/runge/uvnc/ultraSC/rc4/ultravnc_dsm_helper.c
#endif
/*
@@ -100,14 +100,29 @@ static char *usage =
"\n"
"usage: ultravnc_dsm_helper cipher keyfile listenport remotehost:port\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"
" cipher: specify 'msrc4', 'msrc4_sc', 'arc4', 'aesv2',\n"
- " 'aes-cfb', 'aes256', 'blowfish', or '3des'.\n"
+ " 'aes-cfb', 'aes256', 'blowfish', '3des',\n"
+ " 'securevnc'.\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 '.' 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"
" 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"
@@ -119,8 +134,9 @@ static char *usage =
" use 'noultra:rev:...' if both are to be supplied.\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"
+ " 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"
" listenport: port to listen for incoming connection on. (use 0 to connect\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
#include <openssl/evp.h>
#include <openssl/rand.h>
+#include <openssl/rsa.h>
+#include <openssl/err.h>
static const EVP_CIPHER *Cipher;
static const EVP_MD *Digest;
#endif
@@ -229,6 +247,18 @@ static pid_t parent, child;
# 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
@@ -261,7 +291,7 @@ 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, listen_port, connect_port, mbits;
+ int fd, len = 0, listen_port, connect_port, mbits;
q = ciph;
@@ -303,6 +333,10 @@ extern void enc_do(char *ciph, char *keyfile, char *lport, char *rhp) {
} 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, ".") == q) {
/* otherwise, try to guess cipher from key filename: */
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")) {
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);
@@ -336,7 +374,11 @@ extern void enc_do(char *ciph, char *keyfile, char *lport, char *rhp) {
}
/* 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
@@ -406,6 +448,15 @@ extern void enc_do(char *ciph, char *keyfile, char *lport, char *rhp) {
/* check for and read in the key file */
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 (strstr(keyfile, "pw=") == keyfile) {
/* 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 D_keystr[EVP_MAX_KEY_LENGTH];
EVP_CIPHER_CTX E_ctx, D_ctx;
- EVP_CIPHER_CTX *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[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 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(out, 0, BSIZE);
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(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 */
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 */
/*
@@ -558,10 +625,6 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
ENC_PT_DBG(buf, n);
- /* use the encryption context variables below */
- ctx = &E_ctx;
- keystr = E_keystr;
-
} else {
/* 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 */
@@ -644,8 +703,10 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
(unsigned char *) keydata, NULL, encrypt);
}
} 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);
+
EVP_BytesToKey(Cipher, Digest, NULL, (unsigned char *) keydata,
keydata_len, 1, keystr, ivec);
EVP_CIPHER_CTX_init(ctx);
@@ -654,10 +715,12 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
}
} else {
- unsigned char *in_salt;
+ unsigned char *in_salt = NULL;
/* 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 */
fprintf(stderr, "%s: %s - WARNING: no salt\n",
prog, encstr);
@@ -665,7 +728,8 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
} else {
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",
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
* 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);
@@ -710,13 +777,21 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
/* set the cipher & initialize */
/*
- * XXX N.B.: DSM plugin had encrypt=1 for both
- * (i.e. perfectly symmetric)
+ * 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;
@@ -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.
* 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:
+ if (securevnc) {
+ securevnc_setup(conn1, conn2);
+ }
+
/* fork into two processes; one for each direction: */
parent = getpid();
@@ -960,7 +1507,7 @@ extern int main (int argc, char *argv[]) {
char *kf, *q;
if (argc < 4) {
- fprintf(stderr, "%s\n", usage);
+ fprintf(stdout, "%s\n", usage);
exit(1);
}