[patch] mslogon



Hi, folks.

After my initial draft[0] to have this feature implemented, here is my
second attempt. Now it's based on Daniel's rewrite of Diffie-Hellman
implementation, using gnutls (LGPL) functions.

Today I debugged Dan's patch and found out what was crashing my UltraVNC
server: gcry_mpi_print() uses big-endian format, while the format
expected is little endian. I just created a convert() function which
does the conversion, in dh.c.

I tested it and it worked fine in vinagre :-)

[0]-http://mail.gnome.org/archives/gtk-vnc-list/2008-October/msg00000.html
-- 
Jonh Wendell
http://www.bani.com.br
diff --git a/src/Makefile.am b/src/Makefile.am
index df93e21..e2c1bbd 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -20,6 +20,7 @@ gtk_vnc_include_HEADERS = vncdisplay.h
 libgtk_vnc_1_0_la_SOURCES = blt.h blt1.h \
 	coroutine.h \
 	d3des.h d3des.c \
+	dh.h dh.c \
 	gvnc.h gvnc.c \
 	vncdisplay.h vncdisplay.c \
         vncmarshal.h vncmarshal.c \
diff --git a/src/dh.c b/src/dh.c
new file mode 100644
index 0000000..2df5035
--- /dev/null
+++ b/src/dh.c
@@ -0,0 +1,159 @@
+/*
+ * GTK VNC Widget, Diffie Hellman
+ *
+ * Copyright (C) 2008  Red Hat, Inc
+ *
+ * Derived from gnutls_dh.c, also under:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "dh.h"
+#include "utils.h"
+
+/*
+ * General plan, as per gnutls_dh.c
+ *
+ *
+ *  VNC server: X = g ^ x mod p;
+ *  VNC client: Y = g ^ y mod p;
+ *
+ *  Server key = Y ^ x mod p;
+ *  Client key = X ^ y mod p;
+ *
+ * Where
+ *   g == gen
+ *   p == mod
+ *
+ *   y == priv
+ *   Y == pub
+ *  Client key == key
+ *
+ *
+ */
+
+struct gvnc_dh {
+       gcry_mpi_t gen;  /* g */
+       gcry_mpi_t mod;  /* p */
+
+       gcry_mpi_t priv; /* y */
+       gcry_mpi_t pub;  /* Y = g ^ y mod p */
+
+       gcry_mpi_t key;  /*     X ^ y mod p */
+};
+
+#define GVNC_DH_MAX_BITS 31
+
+struct gvnc_dh *gvnc_dh_new(gcry_mpi_t gen, gcry_mpi_t mod)
+{
+       struct gvnc_dh *ret = g_new0(struct gvnc_dh, 1);
+
+       ret->gen = gcry_mpi_copy(gen);
+       ret->mod = gcry_mpi_copy(mod);
+
+       return ret;
+}
+
+
+gcry_mpi_t gvnc_dh_gen_secret(struct gvnc_dh *dh)
+{
+       if (!(dh->priv = gcry_mpi_new(GVNC_DH_MAX_BITS)))
+               abort();
+
+       do {
+               gcry_mpi_randomize (dh->priv, (GVNC_DH_MAX_BITS / 8) * 8, GCRY_STRONG_RANDOM);
+       } while (gcry_mpi_cmp_ui (dh->priv, 0) == 0);
+
+       if (!(dh->pub = gcry_mpi_new(GVNC_DH_MAX_BITS)))
+               abort();
+
+       gcry_mpi_powm(dh->pub, dh->gen, dh->priv, dh->mod);
+
+       return dh->pub;
+}
+
+gcry_mpi_t gvnc_dh_gen_key(struct gvnc_dh *dh, gcry_mpi_t inter)
+{
+       if (!(dh->key = gcry_mpi_new(GVNC_DH_MAX_BITS)))
+               abort();
+
+       gcry_mpi_powm(dh->key, inter, dh->priv, dh->mod);
+
+       return dh->key;
+}
+
+void gvnc_dh_free(struct gvnc_dh *dh)
+{
+       if (dh->key)
+               gcry_mpi_release(dh->key);
+       if (dh->pub)
+               gcry_mpi_release(dh->pub);
+       if (dh->priv)
+               gcry_mpi_release(dh->priv);
+       if (dh->mod)
+               gcry_mpi_release(dh->mod);
+       if (dh->gen)
+               gcry_mpi_release(dh->gen);
+       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)
+{
+  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 gvnc_mpi_to_bytes(const gcry_mpi_t value, guchar* result)
+{
+       gcry_mpi_print(GCRYMPI_FMT_STD, result, 8, NULL, value);
+       convert (result, 8);
+}
+
+gcry_mpi_t gvnc_bytes_to_mpi(const guchar* value)
+{
+       gcry_mpi_t ret;
+       gcry_error_t error;
+
+       error = gcry_mpi_scan(&ret, GCRYMPI_FMT_STD, value, 8, NULL);
+       if (gcry_err_code (error) != GPG_ERR_NO_ERROR)
+         GVNC_DEBUG ("MPI error: %s\n", gcry_strerror (error));
+
+       return ret;
+}
+
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */
diff --git a/src/dh.h b/src/dh.h
new file mode 100644
index 0000000..45e855f
--- /dev/null
+++ b/src/dh.h
@@ -0,0 +1,38 @@
+/*
+ * GTK VNC Widget, Diffie Hellman
+ *
+ * Copyright (C) 2008  Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.0 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#ifndef GTK_VNC_DH_H__
+#define GTK_VNC_DH_H__
+
+#include <glib.h>
+#include <gcrypt.h>
+
+struct gvnc_dh;
+
+struct gvnc_dh *gvnc_dh_new(gcry_mpi_t prime, gcry_mpi_t generator);
+
+gcry_mpi_t gvnc_dh_gen_secret(struct gvnc_dh *dh);
+gcry_mpi_t gvnc_dh_gen_key(struct gvnc_dh *dh, gcry_mpi_t inter);
+void gvnc_dh_free(struct gvnc_dh *dh);
+
+void gvnc_mpi_to_bytes(const gcry_mpi_t value, guchar* result);
+gcry_mpi_t gvnc_bytes_to_mpi(const guchar* value);
+
+#endif
diff --git a/src/gvnc.c b/src/gvnc.c
index b9ccbfe..cf885aa 100644
--- a/src/gvnc.c
+++ b/src/gvnc.c
@@ -51,6 +51,7 @@
 #include <gdk/gdkkeysyms.h>
 
 #include "getaddrinfo.h"
+#include "dh.h"
 
 /* AI_ADDRCONFIG is missing on some systems and gnulib won't provide it
    even if its emulated getaddrinfo() for us . */
@@ -2172,6 +2173,83 @@ static gboolean gvnc_perform_auth_vnc(struct gvnc *gvnc)
 	return gvnc_check_auth_result(gvnc);
 }
 
+/*
+ *   marscha 2006 - Martin Scharpf
+ *   Encrypt bytes[length] in memory using key.
+ *   Key has to be 8 bytes, length a multiple of 8 bytes.
+ */
+static void
+vncEncryptBytes2(unsigned char *where, const int length, unsigned char *key) {
+       int i, j;
+       deskey(key, EN0);
+       for (i = 0; i< 8; i++)
+               where[i] ^= key[i];
+       des(where, where);
+       for (i = 8; i < length; i += 8) {
+               for (j = 0; j < 8; j++)
+                       where[i + j] ^= where[i + j - 8];
+               des(where + i, where + i);
+       }
+}
+
+static gboolean gvnc_perform_auth_mslogon(struct gvnc *gvnc)
+{
+       struct gvnc_dh *dh;
+       guchar gen[8], mod[8], resp[8], pub[8], key[8];
+       gcry_mpi_t genmpi, modmpi, respmpi, pubmpi, keympi;
+       guchar username[256], password[64];
+       guint passwordLen, usernameLen;
+
+       GVNC_DEBUG("Do Challenge\n");
+       if (!gvnc->cred_username)
+               return FALSE;
+       if (!gvnc->cred_password)
+               return FALSE;
+
+       gvnc_read(gvnc, gen, sizeof(gen));
+       gvnc_read(gvnc, mod, sizeof(mod));
+       gvnc_read(gvnc, resp, sizeof(resp));
+
+       genmpi = gvnc_bytes_to_mpi(gen);
+       modmpi = gvnc_bytes_to_mpi(mod);
+       respmpi = gvnc_bytes_to_mpi(resp);
+
+       dh = gvnc_dh_new(genmpi, modmpi);
+
+       pubmpi = gvnc_dh_gen_secret(dh);
+       gvnc_mpi_to_bytes(pubmpi, pub);
+
+       gvnc_write(gvnc, pub, sizeof(pub));
+
+       keympi = gvnc_dh_gen_key(dh, respmpi);
+       gvnc_mpi_to_bytes(keympi, key);
+
+       passwordLen = strlen(gvnc->cred_password);
+       usernameLen = strlen(gvnc->cred_username);
+       if (passwordLen > sizeof(password))
+               passwordLen = sizeof(password);
+       if (usernameLen > sizeof(username))
+               usernameLen = sizeof(username);
+
+       memset(password, 0, sizeof password);
+       memset(username, 0, sizeof username);
+       memcpy(password, gvnc->cred_password, passwordLen);
+       memcpy(username, gvnc->cred_username, usernameLen);
+
+       vncEncryptBytes2(username, sizeof(username), key);
+       vncEncryptBytes2(password, sizeof(password), key);
+
+       gvnc_write(gvnc, username, sizeof(username));
+       gvnc_write(gvnc, password, sizeof(password));
+       gvnc_flush(gvnc);
+
+       gcry_mpi_release(genmpi);
+       gcry_mpi_release(modmpi);
+       gcry_mpi_release(respmpi);
+       gvnc_dh_free (dh);
+
+       return gvnc_check_auth_result(gvnc);
+}
 
 static gboolean gvnc_start_tls(struct gvnc *gvnc, int anonTLS)
 {
@@ -2297,6 +2375,9 @@ gboolean gvnc_wants_credential_password(struct gvnc *gvnc)
                         return TRUE;
         }
 
+       if (gvnc->auth_type == GVNC_AUTH_MSLOGON)
+               return TRUE;
+
         return FALSE;
 }
 
@@ -2309,6 +2390,9 @@ gboolean gvnc_wants_credential_username(struct gvnc *gvnc)
                         return TRUE;
         }
 
+       if (gvnc->auth_type == GVNC_AUTH_MSLOGON)
+               return TRUE;
+
         return FALSE;
 }
 
@@ -2626,6 +2710,9 @@ static gboolean gvnc_perform_auth(struct gvnc *gvnc)
 	case GVNC_AUTH_VENCRYPT:
 		return gvnc_perform_auth_vencrypt(gvnc);
 
+	case GVNC_AUTH_MSLOGON:
+		return gvnc_perform_auth_mslogon(gvnc);
+
 	default:
 		if (gvnc->ops.auth_unsupported)
 			gvnc->ops.auth_unsupported (gvnc->ops_data, gvnc->auth_type);
@@ -2994,6 +3081,7 @@ gboolean gvnc_set_auth_type(struct gvnc *gvnc, unsigned int type)
         }
         if (type != GVNC_AUTH_NONE &&
             type != GVNC_AUTH_VNC &&
+            type != GVNC_AUTH_MSLOGON &&
             type != GVNC_AUTH_TLS &&
             type != GVNC_AUTH_VENCRYPT) {
             	if (gvnc->ops.auth_unsupported)
diff --git a/src/gvnc.h b/src/gvnc.h
index dc65128..496f747 100644
--- a/src/gvnc.h
+++ b/src/gvnc.h
@@ -128,7 +128,8 @@ typedef enum {
 	GVNC_AUTH_TIGHT = 16,
 	GVNC_AUTH_ULTRA = 17,
 	GVNC_AUTH_TLS = 18,  /* Used by VINO */
-	GVNC_AUTH_VENCRYPT = 19 /* Used by VeNCrypt and QEMU */
+	GVNC_AUTH_VENCRYPT = 19, /* Used by VeNCrypt and QEMU */
+	GVNC_AUTH_MSLOGON = 0xfffffffa /* Used by UltraVNC */
 } gvnc_auth;
 
 typedef enum {
diff --git a/src/vncdisplay.c b/src/vncdisplay.c
index 6c608ca..a74494f 100644
--- a/src/vncdisplay.c
+++ b/src/vncdisplay.c
@@ -1910,6 +1910,7 @@ static void vnc_display_init(VncDisplay *display)
 
 	priv->preferable_auths = g_slist_append (priv->preferable_auths, GUINT_TO_POINTER (GVNC_AUTH_VENCRYPT));
 	priv->preferable_auths = g_slist_append (priv->preferable_auths, GUINT_TO_POINTER (GVNC_AUTH_TLS));
+	priv->preferable_auths = g_slist_append (priv->preferable_auths, GUINT_TO_POINTER (GVNC_AUTH_MSLOGON));
 	priv->preferable_auths = g_slist_append (priv->preferable_auths, GUINT_TO_POINTER (GVNC_AUTH_VNC));
 	priv->preferable_auths = g_slist_append (priv->preferable_auths, GUINT_TO_POINTER (GVNC_AUTH_NONE));
 


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