diff options
author | runge <runge@karlrunge.com> | 2009-11-18 18:25:36 -0500 |
---|---|---|
committer | runge <runge@karlrunge.com> | 2009-11-18 18:25:36 -0500 |
commit | 09f63f0395fe103fd1442b2b012b98f2cda2dcd3 (patch) | |
tree | d38a54ba7dc61a229cdf2b83acf21d86eb672cde /x11vnc/enc.h | |
parent | 49cdfb4c1f25b371474f3a355e205471daa08640 (diff) | |
download | libtdevnc-09f63f0395fe103fd1442b2b012b98f2cda2dcd3.tar.gz libtdevnc-09f63f0395fe103fd1442b2b012b98f2cda2dcd3.zip |
x11vnc: -findauth, -auth guess, & etc.
Diffstat (limited to 'x11vnc/enc.h')
-rw-r--r-- | x11vnc/enc.h | 599 |
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); } |