[gtk-vnc] Support Mac OS VNC authentication



commit 416a0dc3592e831409e6e8c18929b785094ef87c
Author: HÃ¥kon Enger <hakon enger gmail com>
Date:   Fri Aug 20 15:12:27 2010 -0300

    Support Mac OS VNC authentication

 src/dh.c            |   47 +++++++--------
 src/dh.h            |    4 +-
 src/vncconnection.c |  154 +++++++++++++++++++++++++++++++++++++++++++++++++--
 src/vncconnection.h |    1 +
 src/vncdisplay.c    |    1 +
 5 files changed, 175 insertions(+), 32 deletions(-)
---
diff --git a/src/dh.c b/src/dh.c
index 3e9ee6f..3ab5e77 100644
--- a/src/dh.c
+++ b/src/dh.c
@@ -111,38 +111,35 @@ void vnc_dh_free(struct vnc_dh *dh)
        g_free(dh);
 }
 
-/* convert from big-endian to little-endian:
-   68 183 219 160 0 0 0 0 becomes
-   0 0 0 0 68 183 219 160 */
-static void convert (unsigned char *input, int size)
+void vnc_mpi_to_bytes(const gcry_mpi_t value, guchar* result, size_t size)
 {
-  int i, count=0;
-
-  for (i = size-1; i >= 0; i--)
-    if (input[i] == 0)
-      count++;
-    else
-      break;
-
-  for (i = 0; i< size-count; i++)
-    {
-      input[i+count] = input[i];
-      input[i] = 0;
-    }
-}
-
-void vnc_mpi_to_bytes(const gcry_mpi_t value, guchar* result)
-{
-       gcry_mpi_print(GCRYMPI_FMT_STD, result, 8, NULL, value);
-       convert (result, 8);
+       gcry_error_t error;
+       size_t len;
+       int i;
+
+       error = gcry_mpi_print(GCRYMPI_FMT_USG, result, size, &len, value);
+       if (gcry_err_code (error) != GPG_ERR_NO_ERROR) {
+	       VNC_DEBUG ("MPI error: %s", gcry_strerror (error));
+	       abort();
+       }
+
+       /* right adjust the result
+	  68 183 219 160 0 0 0 0 becomes
+	  0 0 0 0 68 183 219 160 */
+       for(i=size-1;i>(int)size-1-(int)len;--i) {
+	       result[i] = result[i-size+len];
+       }
+       for(;i>=0;--i) {
+	       result[i] = 0;
+       }
 }
 
-gcry_mpi_t vnc_bytes_to_mpi(const guchar* value)
+gcry_mpi_t vnc_bytes_to_mpi(const guchar* value, size_t size)
 {
        gcry_mpi_t ret;
        gcry_error_t error;
 
-       error = gcry_mpi_scan(&ret, GCRYMPI_FMT_STD, value, 8, NULL);
+       error = gcry_mpi_scan(&ret, GCRYMPI_FMT_USG, value, size, NULL);
        if (gcry_err_code (error) != GPG_ERR_NO_ERROR)
          VNC_DEBUG ("MPI error: %s", gcry_strerror (error));
 
diff --git a/src/dh.h b/src/dh.h
index 7585df1..ed4fe14 100644
--- a/src/dh.h
+++ b/src/dh.h
@@ -32,7 +32,7 @@ gcry_mpi_t vnc_dh_gen_secret(struct vnc_dh *dh);
 gcry_mpi_t vnc_dh_gen_key(struct vnc_dh *dh, gcry_mpi_t inter);
 void vnc_dh_free(struct vnc_dh *dh);
 
-void vnc_mpi_to_bytes(const gcry_mpi_t value, guchar* result);
-gcry_mpi_t vnc_bytes_to_mpi(const guchar* value);
+void vnc_mpi_to_bytes(const gcry_mpi_t value, guchar* result, size_t size);
+gcry_mpi_t vnc_bytes_to_mpi(const guchar* value, size_t size);
 
 #endif
diff --git a/src/vncconnection.c b/src/vncconnection.c
index 7525b8d..566f4da 100644
--- a/src/vncconnection.c
+++ b/src/vncconnection.c
@@ -2989,19 +2989,19 @@ static gboolean vnc_connection_perform_auth_mslogon(VncConnection *conn)
 	vnc_connection_read(conn, mod, sizeof(mod));
 	vnc_connection_read(conn, resp, sizeof(resp));
 
-	genmpi = vnc_bytes_to_mpi(gen);
-	modmpi = vnc_bytes_to_mpi(mod);
-	respmpi = vnc_bytes_to_mpi(resp);
+	genmpi = vnc_bytes_to_mpi(gen,sizeof(gen));
+	modmpi = vnc_bytes_to_mpi(mod,sizeof(mod));
+	respmpi = vnc_bytes_to_mpi(resp,sizeof(resp));
 
 	dh = vnc_dh_new(genmpi, modmpi);
 
 	pubmpi = vnc_dh_gen_secret(dh);
-	vnc_mpi_to_bytes(pubmpi, pub);
+	vnc_mpi_to_bytes(pubmpi, pub, sizeof(pub));
 
 	vnc_connection_write(conn, pub, sizeof(pub));
 
 	keympi = vnc_dh_gen_key(dh, respmpi);
-	vnc_mpi_to_bytes(keympi, key);
+	vnc_mpi_to_bytes(keympi, key, sizeof(key));
 
 	passwordLen = strlen(priv->cred_password);
 	usernameLen = strlen(priv->cred_username);
@@ -3030,6 +3030,146 @@ static gboolean vnc_connection_perform_auth_mslogon(VncConnection *conn)
 	return vnc_connection_check_auth_result(conn);
 }
 
+static gboolean vnc_connection_perform_auth_ard(VncConnection *conn)
+{
+	VncConnectionPrivate *priv = conn->priv;
+	struct vnc_dh *dh;
+	guchar gen[2], len[2];
+	size_t keylen;
+	guchar *mod, *resp, *pub, *key, *shared;
+	gcry_mpi_t genmpi, modmpi, respmpi, pubmpi, keympi;
+	guchar userpass[128], ciphertext[128];
+	guint passwordLen, usernameLen;
+	gcry_md_hd_t md5;
+	gcry_cipher_hd_t aes;
+	gcry_error_t error;
+
+	VNC_DEBUG("Do Challenge");
+	priv->want_cred_password = TRUE;
+	priv->want_cred_username = TRUE;
+	priv->want_cred_x509 = FALSE;
+	if (!vnc_connection_gather_credentials(conn))
+		return FALSE;
+
+	vnc_connection_read(conn, gen, sizeof(gen));
+	vnc_connection_read(conn, len, sizeof(len));
+
+	keylen = 256*len[0] + len[1];
+	mod = malloc(keylen);
+	if (mod == NULL) {
+		VNC_DEBUG("malloc failed\n");
+		return FALSE;
+	}
+	resp = malloc(keylen);
+	if (resp == NULL) {
+		free(mod);
+		VNC_DEBUG("malloc failed\n");
+		return FALSE;
+	}
+	pub = malloc(keylen);
+	if (pub == NULL) {
+		free(resp);
+		free(mod);
+		VNC_DEBUG("malloc failed\n");
+		return FALSE;
+	}
+	key = malloc(keylen);
+	if (key == NULL) {
+		free(pub);
+		free(resp);
+		free(mod);
+		VNC_DEBUG("malloc failed\n");
+		return FALSE;
+	}
+
+	vnc_connection_read(conn, mod, keylen);
+	vnc_connection_read(conn, resp, keylen);
+
+	genmpi = vnc_bytes_to_mpi(gen,sizeof(gen));
+	modmpi = vnc_bytes_to_mpi(mod,keylen);
+	respmpi = vnc_bytes_to_mpi(resp,keylen);
+
+	dh = vnc_dh_new(genmpi, modmpi);
+
+	pubmpi = vnc_dh_gen_secret(dh);
+	vnc_mpi_to_bytes(pubmpi, pub, keylen);
+
+	keympi = vnc_dh_gen_key(dh, respmpi);
+	vnc_mpi_to_bytes(keympi, key, keylen);
+
+	error=gcry_md_open(&md5, GCRY_MD_MD5, 0);
+	if (gcry_err_code (error) != GPG_ERR_NO_ERROR) {
+		VNC_DEBUG("gcry_md_open error: %s\n", gcry_strerror(error));
+		free(pub);
+		free(resp);
+		free(mod);
+		return FALSE;
+	}
+	gcry_md_write(md5, key, keylen);
+	error=gcry_md_final(md5);
+	if (gcry_err_code (error) != GPG_ERR_NO_ERROR) {
+		VNC_DEBUG("gcry_md_final error: %s\n", gcry_strerror(error));
+		free(pub);
+		free(resp);
+		free(mod);
+		return FALSE;
+	}
+	shared = gcry_md_read(md5, GCRY_MD_MD5);
+
+	passwordLen = strlen(priv->cred_password)+1;
+	usernameLen = strlen(priv->cred_username)+1;
+	if (passwordLen > sizeof(userpass)/2)
+		passwordLen = sizeof(userpass)/2;
+	if (usernameLen > sizeof(userpass)/2)
+		usernameLen = sizeof(userpass)/2;
+
+	gcry_randomize(userpass, sizeof(userpass), GCRY_STRONG_RANDOM);
+	memcpy(userpass, priv->cred_username, usernameLen);
+	memcpy(userpass+sizeof(userpass)/2, priv->cred_password, passwordLen);
+	
+	error=gcry_cipher_open(&aes, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_ECB, 0);
+	if (gcry_err_code (error) != GPG_ERR_NO_ERROR) {
+		VNC_DEBUG("gcry_cipher_open error: %s\n", gcry_strerror(error));
+		free(pub);
+		free(resp);
+		free(mod);
+		return FALSE;
+	}
+	error=gcry_cipher_setkey(aes, shared, 16);
+	if (gcry_err_code (error) != GPG_ERR_NO_ERROR) {
+		VNC_DEBUG("gcry_cipher_setkey error: %s\n", gcry_strerror(error));
+		free(pub);
+		free(resp);
+		free(mod);
+		return FALSE;
+	}
+	error=gcry_cipher_encrypt(aes, ciphertext, sizeof(ciphertext), userpass, sizeof(userpass));
+	if (gcry_err_code (error) != GPG_ERR_NO_ERROR) {
+		VNC_DEBUG("gcry_cipher_encrypt error: %s\n", gcry_strerror(error));
+		free(pub);
+		free(resp);
+		free(mod);
+		return FALSE;
+	}
+
+	vnc_connection_write(conn, ciphertext, sizeof(ciphertext));
+	vnc_connection_write(conn, pub, keylen);
+	vnc_connection_flush(conn);
+
+	free(mod);
+	free(resp);
+	free(pub);
+	free(key);
+	gcry_md_close(md5);
+	gcry_mpi_release(genmpi);
+	gcry_mpi_release(modmpi);
+	gcry_mpi_release(respmpi);
+	vnc_dh_free (dh);
+
+	return vnc_connection_check_auth_result(conn);
+}
+
+
 #if HAVE_SASL
 /*
  * NB, keep in sync with similar method in qemud/remote.c
@@ -3918,6 +4058,9 @@ static gboolean vnc_connection_perform_auth(VncConnection *conn)
 	case VNC_CONNECTION_AUTH_MSLOGON:
 		return vnc_connection_perform_auth_mslogon(conn);
 
+	case VNC_CONNECTION_AUTH_ARD:
+		return vnc_connection_perform_auth_ard(conn);
+
 	default:
 		{
 			struct signal_data sigdata;
@@ -4610,6 +4753,7 @@ gboolean vnc_connection_set_auth_type(VncConnection *conn, unsigned int type)
         if (type != VNC_CONNECTION_AUTH_NONE &&
             type != VNC_CONNECTION_AUTH_VNC &&
             type != VNC_CONNECTION_AUTH_MSLOGON &&
+            type != VNC_CONNECTION_AUTH_ARD &&
             type != VNC_CONNECTION_AUTH_TLS &&
             type != VNC_CONNECTION_AUTH_VENCRYPT &&
             type != VNC_CONNECTION_AUTH_SASL) {
diff --git a/src/vncconnection.h b/src/vncconnection.h
index 5023f80..e189351 100644
--- a/src/vncconnection.h
+++ b/src/vncconnection.h
@@ -124,6 +124,7 @@ typedef enum {
 	VNC_CONNECTION_AUTH_TLS = 18,  /* Used by VINO */
 	VNC_CONNECTION_AUTH_VENCRYPT = 19, /* Used by VeNCrypt and QEMU */
  	VNC_CONNECTION_AUTH_SASL = 20, /* SASL type used by VINO and QEMU */
+	VNC_CONNECTION_AUTH_ARD = 30, /* Apple remote desktop (screen sharing) */
 	VNC_CONNECTION_AUTH_MSLOGON = 0xfffffffa, /* Used by UltraVNC */
 } VncConnectionAuth;
 
diff --git a/src/vncdisplay.c b/src/vncdisplay.c
index 5c7845e..9ef9841 100644
--- a/src/vncdisplay.c
+++ b/src/vncdisplay.c
@@ -1857,6 +1857,7 @@ static void vnc_display_init(VncDisplay *display)
 	 */
 	priv->preferable_auths = g_slist_append (priv->preferable_auths, GUINT_TO_POINTER (VNC_CONNECTION_AUTH_SASL));
 	priv->preferable_auths = g_slist_append (priv->preferable_auths, GUINT_TO_POINTER (VNC_CONNECTION_AUTH_MSLOGON));
+	priv->preferable_auths = g_slist_append (priv->preferable_auths, GUINT_TO_POINTER (VNC_CONNECTION_AUTH_ARD));
 	priv->preferable_auths = g_slist_append (priv->preferable_auths, GUINT_TO_POINTER (VNC_CONNECTION_AUTH_VNC));
 
 	/*



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]