[gtk-vnc-devel] PATCH: Authentication API



This patch adds a method for requesting authentication credentials from
the end application. This time around all the credentials are asked for
at once. The signal 'vnc-auth-credential' has a parameter which is a
GValueArray, this in turn contains GEnumValues. A little tedious, but it
marshalls well into python & this is just the way GTK likes to do arrays.
The example programs are re-done so that they dynamically build up a 
dialog box to request all credentials at once.

 examples/gvncviewer.c      |   68 +++++-
 examples/gvncviewer.py     |   35 +++
 src/gvnc.c                 |  498 +++++++++++++++++++++++++++++++++------------
 src/gvnc.h                 |   36 +++
 src/libgtk-vnc_sym.version |    5 
 src/vncdisplay.c           |  127 ++++++++++-
 src/vncdisplay.h           |   11 
 src/vncmodule.c            |    2 
 8 files changed, 645 insertions(+), 137 deletions(-)

Dan.
-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 
diff -r d5a758344261 examples/gvncviewer.c
--- a/examples/gvncviewer.c	Thu Jul 19 21:22:05 2007 -0400
+++ b/examples/gvncviewer.c	Thu Jul 19 21:31:34 2007 -0400
@@ -81,6 +81,69 @@ static void send_cab(GtkWidget *menu G_G
 	guint keys[] = { GDK_Control_L, GDK_Alt_L, GDK_BackSpace };
 	printf("Sending Ctrl+Alt+Backspace\n");
 	vnc_display_send_keys(VNC_DISPLAY(vnc), keys, sizeof(keys)/sizeof(keys[0]));
+}
+
+static void vnc_credential(GtkWidget *vnc, GValueArray *credList)
+{
+	GtkWidget *dialog, **label, **entry, *box, *vbox;
+	int response;
+	unsigned int i;
+
+	printf("Got credential request for %d credential(s)\n", credList->n_values);
+
+	dialog = gtk_dialog_new_with_buttons("Authentication required",
+					     NULL,
+					     0,
+					     GTK_STOCK_CANCEL,
+					     GTK_RESPONSE_CANCEL,
+					     GTK_STOCK_OK,
+					     GTK_RESPONSE_OK,
+					     NULL);
+	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
+
+	box = gtk_table_new(credList->n_values, 2, FALSE);
+	label = g_new(GtkWidget *, credList->n_values);
+	entry = g_new(GtkWidget *, credList->n_values);
+
+	for (i = 0 ; i < credList->n_values ; i++) {
+		GValue *cred = g_value_array_get_nth(credList, i);
+		int credType = g_value_get_enum(cred);
+		switch (credType) {
+		case VNC_DISPLAY_CREDENTIAL_USERNAME:
+			label[i] = gtk_label_new("Username:");
+			break;
+		default:
+			label[i] = gtk_label_new("Password:");
+			break;
+		}
+		entry[i] = gtk_entry_new();
+
+		gtk_table_attach(GTK_TABLE(box), label[i], 0, 1, i, i+1, GTK_SHRINK, GTK_SHRINK, 3, 3);
+		gtk_table_attach(GTK_TABLE(box), entry[i], 1, 2, i, i+1, GTK_SHRINK, GTK_SHRINK, 3, 3);
+	}
+
+
+	vbox = gtk_bin_get_child(GTK_BIN(dialog));
+
+	gtk_container_add(GTK_CONTAINER(vbox), box);
+
+	gtk_widget_show_all(dialog);
+	response = gtk_dialog_run(GTK_DIALOG(dialog));
+	gtk_widget_hide(GTK_WIDGET(dialog));
+
+	if (response == GTK_RESPONSE_OK) {
+		for (i = 0 ; i < credList->n_values ; i++) {
+			GValue *cred = g_value_array_get_nth(credList, i);
+			int credType = g_value_get_enum(cred);
+			const char *data = gtk_entry_get_text(GTK_ENTRY(entry[i]));
+			vnc_display_set_credential(VNC_DISPLAY(vnc), credType, data);
+		}
+	} else {
+		printf("Aborting connection\n");
+		vnc_display_close(VNC_DISPLAY(vnc));
+	}
+
+	gtk_widget_destroy(GTK_WIDGET(dialog));
 }
 
 int main(int argc, char **argv)
@@ -135,7 +198,7 @@ int main(int argc, char **argv)
 	gtk_widget_realize(vnc);
 
 	if (argc == 3)
-		vnc_display_set_password(VNC_DISPLAY(vnc), argv[2]);
+		vnc_display_set_credential(VNC_DISPLAY(vnc), VNC_DISPLAY_CREDENTIAL_PASSWORD, argv[2]);
 
 	snprintf(hostname, sizeof(hostname), "%s", argv[1]);
 	display = strchr(hostname, ':');
@@ -159,7 +222,8 @@ int main(int argc, char **argv)
 			   GTK_SIGNAL_FUNC(vnc_initialized), window);
 	gtk_signal_connect(GTK_OBJECT(vnc), "vnc-disconnected",
 			   GTK_SIGNAL_FUNC(vnc_disconnected), NULL);
-
+	g_signal_connect(GTK_OBJECT(vnc), "vnc-auth-credential",
+			   GTK_SIGNAL_FUNC(vnc_credential), NULL);
 
 	gtk_signal_connect(GTK_OBJECT(vnc), "vnc-pointer-grab",
 			   GTK_SIGNAL_FUNC(vnc_grab), window);
diff -r d5a758344261 examples/gvncviewer.py
--- a/examples/gvncviewer.py	Thu Jul 19 21:22:05 2007 -0400
+++ b/examples/gvncviewer.py	Thu Jul 19 21:22:07 2007 -0400
@@ -51,6 +51,38 @@ def send_cab(src, vnc):
     print "Send Ctrl+Alt+BackSpace"
     vnc.send_keys(["Control_L", "Alt_L", "BackSpace"])
 
+def vnc_auth_cred(src, credList):
+    dialog = gtk.Dialog("Authentication required", None, 0, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK))
+    label = []
+    entry = []
+
+    box = gtk.Table(2, len(credList))
+
+    for i in range(len(credList)):
+        if credList[i] == gtkvnc.CREDENTIAL_USERNAME:
+            label.append(gtk.Label("Username:"))
+        else:
+            label.append(gtk.Label("Password:"))
+        entry.append(gtk.Entry())
+
+        box.attach(label[i], 0, 1, i, i+1, 0, 0, 3, 3)
+        box.attach(entry[i], 1, 2, i, i+1, 0, 0, 3, 3)
+
+    vbox = dialog.get_child()
+    vbox.add(box)
+
+    dialog.show_all()
+    res = dialog.run()
+    dialog.hide()
+
+    if res == gtk.RESPONSE_OK:
+        for i in range(len(credList)):
+            data = entry[i].get_text()
+            src.set_credential(credList[i], data)
+    else:
+        src.close()
+    dialog.destroy()
+
 window = gtk.Window()
 vnc = gtkvnc.Display()
 
@@ -89,7 +121,7 @@ vnc.set_keyboard_grab(True)
 #v.set_pointer_local(True)
 
 if len(sys.argv) == 3:
-    vnc.set_password(sys.argv[2])
+    vnc.set_credential(gtkvnc.CREDENTIAL_PASSWORD, sys.argv[3])
 
 disp = sys.argv[1].find(":")
 if disp != -1:
@@ -107,5 +139,6 @@ vnc.connect("vnc-connected", vnc_connect
 vnc.connect("vnc-connected", vnc_connected)
 vnc.connect("vnc-initialized", vnc_initialized, window)
 vnc.connect("vnc-disconnected", vnc_disconnected)
+vnc.connect("vnc-auth-credential", vnc_auth_cred)
 
 gtk.main()
diff -r d5a758344261 src/gvnc.c
--- a/src/gvnc.c	Thu Jul 19 21:22:05 2007 -0400
+++ b/src/gvnc.c	Thu Jul 19 21:22:44 2007 -0400
@@ -38,57 +38,12 @@
 #define KEY_FILE "key.pem"
 #define CERT_FILE "cert.pem"
 
-static gboolean g_io_wait_helper(GIOChannel *channel G_GNUC_UNUSED,
-				 GIOCondition cond,
-				 gpointer data)
-{
-	struct coroutine *to = data;
-	yieldto(to, &cond);
-	return FALSE;
-}
-
-static GIOCondition g_io_wait(GIOChannel *channel, GIOCondition cond)
-{
-	GIOCondition *ret;
-
-	g_io_add_watch(channel, cond, g_io_wait_helper, coroutine_self());
-	ret = yield(NULL);
-
-	return *ret;
-}
-
 struct wait_queue
 {
 	gboolean waiting;
 	struct coroutine *context;
 };
 
-static GIOCondition g_io_wait_interruptable(struct wait_queue *wait,
-					    GIOChannel *channel,
-					    GIOCondition cond)
-{
-	GIOCondition *ret;
-	gint id;
-
-	wait->context = coroutine_self();
-	id = g_io_add_watch(channel, cond, g_io_wait_helper, wait->context);
-
-	wait->waiting = TRUE;
-	ret = yield(NULL);
-	wait->waiting = FALSE;
-
-	if (ret == NULL) {
-		g_source_remove(id);
-		return 0;
-	} else
-		return *ret;
-}
-
-static void g_io_wakeup(struct wait_queue *wait)
-{
-	if (wait->waiting)
-		yieldto(wait->context, NULL);
-}
 
 typedef void gvnc_blt_func(struct gvnc *, uint8_t *, int, int, int, int, int);
 
@@ -96,6 +51,25 @@ typedef void gvnc_hextile_func(struct gv
 			       uint16_t x, uint16_t y,
 			       uint16_t width, uint16_t height,
 			       uint8_t *fg, uint8_t *bg);
+
+/*
+ * A special GSource impl which allows us to wait on a certain
+ * condition to be satisified. This is effectively a boolean test
+ * run on each iteration of the main loop. So whenever a file has
+ * new I/O, or a timer occurrs, etc we'll do the check. This is
+ * pretty efficient compared to a normal GLib Idle func which has
+ * to busy wait on a timeout, since our condition is only checked
+ * when some other source's state changes
+ */
+typedef gboolean (*g_condition_wait_func)(gpointer);
+
+struct g_condition_wait_source
+{
+        GSource src;
+        struct coroutine *co;
+	g_condition_wait_func func;
+	gpointer data;
+};
 
 struct gvnc
 {
@@ -113,6 +87,12 @@ struct gvnc
 	int minor;
 	gnutls_session_t tls_session;
 
+	/* Auth related params */
+	unsigned int auth_type;
+	unsigned int auth_subtype;
+	char *cred_username;
+	char *cred_password;
+
 	char read_buffer[4096];
 	size_t read_offset;
 	size_t read_size;
@@ -144,28 +124,6 @@ struct gvnc
 	int xmit_buffer_size;
 };
 
-enum {
-  GVNC_AUTH_INVALID = 0,
-  GVNC_AUTH_NONE = 1,
-  GVNC_AUTH_VNC = 2,
-  GVNC_AUTH_RA2 = 5,
-  GVNC_AUTH_RA2NE = 6,
-  GVNC_AUTH_TIGHT = 16,
-  GVNC_AUTH_ULTRA = 17,
-  GVNC_AUTH_TLS = 18,
-  GVNC_AUTH_VENCRYPT = 19
-};
-
-enum {
-  GVNC_AUTH_VENCRYPT_PLAIN = 256,
-  GVNC_AUTH_VENCRYPT_TLSNONE = 257,
-  GVNC_AUTH_VENCRYPT_TLSVNC = 258,
-  GVNC_AUTH_VENCRYPT_TLSPLAIN = 259,
-  GVNC_AUTH_VENCRYPT_X509NONE = 260,
-  GVNC_AUTH_VENCRYPT_X509VNC = 261,
-  GVNC_AUTH_VENCRYPT_X509PLAIN = 262,
-};
-
 
 #define DEBUG 0
 #if DEBUG
@@ -183,6 +141,122 @@ static void debug_log(int level, const c
 
 #define nibhi(a) (((a) >> 4) & 0x0F)
 #define niblo(a) ((a) & 0x0F)
+
+/* Main loop helper functions */
+static gboolean g_io_wait_helper(GIOChannel *channel G_GNUC_UNUSED,
+				 GIOCondition cond,
+				 gpointer data)
+{
+	struct coroutine *to = data;
+	yieldto(to, &cond);
+	return FALSE;
+}
+
+static GIOCondition g_io_wait(GIOChannel *channel, GIOCondition cond)
+{
+	GIOCondition *ret;
+
+	g_io_add_watch(channel, cond, g_io_wait_helper, coroutine_self());
+	ret = yield(NULL);
+
+	return *ret;
+}
+
+
+static GIOCondition g_io_wait_interruptable(struct wait_queue *wait,
+					    GIOChannel *channel,
+					    GIOCondition cond)
+{
+	GIOCondition *ret;
+	gint id;
+
+	wait->context = coroutine_self();
+	id = g_io_add_watch(channel, cond, g_io_wait_helper, wait->context);
+
+	wait->waiting = TRUE;
+	ret = yield(NULL);
+	wait->waiting = FALSE;
+
+	if (ret == NULL) {
+		g_source_remove(id);
+		return 0;
+	} else
+		return *ret;
+}
+
+static void g_io_wakeup(struct wait_queue *wait)
+{
+	if (wait->waiting)
+		yieldto(wait->context, NULL);
+}
+
+
+/*
+ * Call immediately before the main loop does an iteration. Returns
+ * true if the condition we're checking is ready for dispatch
+ */
+static gboolean g_condition_wait_prepare(GSource *src,
+					 int *timeout) {
+        struct g_condition_wait_source *vsrc = (struct g_condition_wait_source *)src;
+        *timeout = -1;
+        return vsrc->func(vsrc->data);
+}
+
+/*
+ * Call immediately after the main loop does an iteration. Returns
+ * true if the condition we're checking is ready for dispatch
+ */
+static gboolean g_condition_wait_check(GSource *src) {
+        struct g_condition_wait_source *vsrc = (struct g_condition_wait_source *)src;
+        return vsrc->func(vsrc->data);
+}
+
+static gboolean g_condition_wait_dispatch(GSource *src G_GNUC_UNUSED,
+					  GSourceFunc cb,
+					  gpointer data) {
+        return cb(data);
+}
+
+GSourceFuncs waitFuncs = {
+        .prepare = g_condition_wait_prepare,
+        .check = g_condition_wait_check,
+        .dispatch = g_condition_wait_dispatch,
+  };
+
+static gboolean g_condition_wait_helper(gpointer data)
+{
+        struct coroutine *co = (struct coroutine *)data;
+        yieldto(co, NULL);
+        return FALSE;
+}
+
+static gboolean g_condition_wait(g_condition_wait_func func, gpointer data)
+{
+	GSource *src;
+	struct g_condition_wait_source *vsrc;
+
+	/* Short-circuit check in case we've got it ahead of time */
+	if (func(data)) {
+		return TRUE;
+	}
+
+	/*
+	 * Don't have it, so yield to the main loop, checking the condition
+	 * on each iteration of the main loop
+	 */
+	src = g_source_new(&waitFuncs, sizeof(struct g_condition_wait_source));
+	vsrc = (struct g_condition_wait_source *)src;
+
+	vsrc->func = func;
+	vsrc->data = data;
+	vsrc->co = coroutine_self();
+
+	g_source_attach(src, NULL);
+	g_source_set_callback(src, g_condition_wait_helper, coroutine_self(), NULL);
+	yield(NULL);
+	return TRUE;
+}
+
 
 
 /* IO functions */
@@ -912,7 +986,8 @@ static void gvnc_update(struct gvnc *gvn
 {
 	if (gvnc->has_error || !gvnc->ops.update)
 		return;
-	gvnc->has_error = !gvnc->ops.update(gvnc->ops_data, x, y, width, height);
+	if (!gvnc->ops.update(gvnc->ops_data, x, y, width, height))
+		gvnc->has_error = TRUE;
 }
 
 static void gvnc_set_color_map_entry(struct gvnc *gvnc, uint16_t color,
@@ -921,15 +996,17 @@ static void gvnc_set_color_map_entry(str
 {
 	if (gvnc->has_error || !gvnc->ops.set_color_map_entry)
 		return;
-	gvnc->has_error = !gvnc->ops.set_color_map_entry(gvnc->ops_data, color,
-							 red, green, blue);
+	if (!gvnc->ops.set_color_map_entry(gvnc->ops_data, color,
+					    red, green, blue))
+		gvnc->has_error = TRUE;
 }
 
 static void gvnc_bell(struct gvnc *gvnc)
 {
 	if (gvnc->has_error || !gvnc->ops.bell)
 		return;
-	gvnc->has_error = !gvnc->ops.bell(gvnc->ops_data);
+	if (!gvnc->ops.bell(gvnc->ops_data))
+		gvnc->has_error = TRUE;
 }
 
 static void gvnc_server_cut_text(struct gvnc *gvnc, const void *data,
@@ -937,28 +1014,32 @@ static void gvnc_server_cut_text(struct 
 {
 	if (gvnc->has_error || !gvnc->ops.server_cut_text)
 		return;
-	gvnc->has_error = !gvnc->ops.server_cut_text(gvnc->ops_data, data, len);
+	if (!gvnc->ops.server_cut_text(gvnc->ops_data, data, len))
+		gvnc->has_error = TRUE;
 }
 
 static void gvnc_resize(struct gvnc *gvnc, int width, int height)
 {
 	if (gvnc->has_error || !gvnc->ops.resize)
 		return;
-	gvnc->has_error = !gvnc->ops.resize(gvnc->ops_data, width, height);
+	if (!gvnc->ops.resize(gvnc->ops_data, width, height))
+		gvnc->has_error = TRUE;
 }
 
 static void gvnc_pointer_type_change(struct gvnc *gvnc, int absolute)
 {
 	if (gvnc->has_error || !gvnc->ops.pointer_type_change)
 		return;
-	gvnc->has_error = !gvnc->ops.pointer_type_change(gvnc->ops_data, absolute);
+	if (!gvnc->ops.pointer_type_change(gvnc->ops_data, absolute))
+		gvnc->has_error = TRUE;
 }
 
 static void gvnc_shared_memory_rmid(struct gvnc *gvnc, int shmid)
 {
 	if (gvnc->has_error || !gvnc->ops.shared_memory_rmid)
 		return;
-	gvnc->has_error = !gvnc->ops.shared_memory_rmid(gvnc->ops_data, shmid);
+	if (!gvnc->ops.shared_memory_rmid(gvnc->ops_data, shmid))
+		gvnc->has_error = TRUE;
 }
 
 static void gvnc_framebuffer_update(struct gvnc *gvnc, int32_t etype,
@@ -1125,19 +1206,19 @@ static gboolean gvnc_check_auth_result(s
 	return FALSE;
 }
 
-static gboolean gvnc_perform_auth_vnc(struct gvnc *gvnc, const char *password)
+static gboolean gvnc_perform_auth_vnc(struct gvnc *gvnc)
 {
 	uint8_t challenge[16];
 	uint8_t key[8];
 
 	GVNC_DEBUG("Do Challenge\n");
-	if (!password)
+	if (!gvnc->cred_password)
 		return FALSE;
 
 	gvnc_read(gvnc, challenge, 16);
 
 	memset(key, 0, 8);
-	strncpy((char*)key, (char*)password, 8);
+	strncpy((char*)key, (char*)gvnc->cred_password, 8);
 
 	deskey(key, EN0);
 	des(challenge, challenge);
@@ -1245,9 +1326,82 @@ static gboolean gvnc_start_tls(struct gv
 	}
 }
 
-static gboolean gvnc_perform_auth_vencrypt(struct gvnc *gvnc, const char *password)
-{
-	int major, minor, status, wantAuth = GVNC_AUTH_INVALID, anonTLS;
+gboolean gvnc_wants_credential_password(struct gvnc *gvnc)
+{
+        if (gvnc->auth_type == GVNC_AUTH_VNC)
+                return TRUE;
+
+        if (gvnc->auth_type == GVNC_AUTH_VENCRYPT) {
+                if (gvnc->auth_subtype == GVNC_AUTH_VENCRYPT_PLAIN ||
+                    gvnc->auth_subtype == GVNC_AUTH_VENCRYPT_TLSVNC ||
+                    gvnc->auth_subtype == GVNC_AUTH_VENCRYPT_TLSPLAIN ||
+                    gvnc->auth_subtype == GVNC_AUTH_VENCRYPT_X509VNC ||
+                    gvnc->auth_subtype == GVNC_AUTH_VENCRYPT_X509PLAIN)
+                        return TRUE;
+        }
+
+        return FALSE;
+}
+
+gboolean gvnc_wants_credential_username(struct gvnc *gvnc)
+{
+        if (gvnc->auth_type == GVNC_AUTH_VENCRYPT) {
+                if (gvnc->auth_subtype == GVNC_AUTH_VENCRYPT_PLAIN ||
+                    gvnc->auth_subtype == GVNC_AUTH_VENCRYPT_TLSPLAIN ||
+                    gvnc->auth_subtype == GVNC_AUTH_VENCRYPT_X509PLAIN)
+                        return TRUE;
+        }
+
+        return FALSE;
+}
+
+static gboolean gvnc_has_credentials(gpointer data)
+{
+	struct gvnc *gvnc = (struct gvnc *)data;
+
+	if (gvnc->has_error)
+		return TRUE;
+	if (gvnc_wants_credential_username(gvnc) && !gvnc->cred_username)
+		return FALSE;
+	if (gvnc_wants_credential_password(gvnc) && !gvnc->cred_password)
+		return FALSE;
+	return TRUE;
+}
+
+static gboolean gvnc_gather_credentials(struct gvnc *gvnc)
+{
+	if (!gvnc_has_credentials(gvnc)) {
+		GVNC_DEBUG("Requesting missing credentials\n");
+		if (gvnc->has_error || !gvnc->ops.auth_cred) {
+			gvnc->has_error = TRUE;
+			return TRUE;
+		}
+		if (!gvnc->ops.auth_cred(gvnc->ops_data))
+		    gvnc->has_error = TRUE;
+		if (gvnc->has_error)
+			return TRUE;
+		GVNC_DEBUG("Waiting for missing credentials\n");
+		g_condition_wait(gvnc_has_credentials, gvnc);
+		GVNC_DEBUG("Got all credentials\n");
+	}
+	return gvnc_has_error(gvnc);
+}
+
+static gboolean gvnc_has_auth_subtype(gpointer data)
+{
+	struct gvnc *gvnc = (struct gvnc *)data;
+
+	if (gvnc->has_error)
+		return TRUE;
+	if (gvnc->auth_subtype == GVNC_AUTH_INVALID)
+		return FALSE;
+	return TRUE;
+}
+
+
+static gboolean gvnc_perform_auth_vencrypt(struct gvnc *gvnc)
+{
+	int major, minor, status, anonTLS;
 	unsigned int nauth, i;
 	unsigned int auth[20];
 
@@ -1283,41 +1437,48 @@ static gboolean gvnc_perform_auth_vencry
 		GVNC_DEBUG("Possible auth %d\n", auth[i]);
 	}
 
+	if (gvnc->has_error || !gvnc->ops.auth_subtype)
+		return FALSE;
+
+	if (!gvnc->ops.auth_subtype(gvnc->ops_data, nauth, auth))
+		gvnc->has_error = TRUE;
 	if (gvnc->has_error)
 		return FALSE;
 
-	for (i = 0 ; i < nauth ; i++) {
-		if (auth[i] == GVNC_AUTH_VENCRYPT_TLSNONE ||
-		    auth[i] == GVNC_AUTH_VENCRYPT_TLSPLAIN ||
-		    auth[i] == GVNC_AUTH_VENCRYPT_TLSVNC ||
-		    auth[i] == GVNC_AUTH_VENCRYPT_X509NONE ||
-		    auth[i] == GVNC_AUTH_VENCRYPT_X509PLAIN ||
-		    auth[i] == GVNC_AUTH_VENCRYPT_X509VNC) {
-			wantAuth = auth[i];
-			break;
-		}
-	}
-
-	if (wantAuth == GVNC_AUTH_VENCRYPT_PLAIN) {
+	GVNC_DEBUG("Waiting for auth subtype\n");
+	g_condition_wait(gvnc_has_auth_subtype, gvnc);
+	if (gvnc->has_error)
+		return FALSE;
+
+	GVNC_DEBUG("Choose auth %d\n", gvnc->auth_subtype);
+
+	if (gvnc_gather_credentials(gvnc))
+		return FALSE;
+
+#if !DEBUG
+	if (gvnc->auth_subtype == GVNC_AUTH_VENCRYPT_PLAIN) {
 		GVNC_DEBUG("Cowardly refusing to transmit plain text password\n");
 		return FALSE;
 	}
-
-	GVNC_DEBUG("Choose auth %d\n", wantAuth);
-	gvnc_write_u32(gvnc, wantAuth);
+#endif
+
+	gvnc_write_u32(gvnc, gvnc->auth_subtype);
 	gvnc_flush(gvnc);
 	status = gvnc_read_u8(gvnc);
 	if (status != 1) {
-		GVNC_DEBUG("Server refused VeNCrypt auth %d %d\n", wantAuth, status);
-		return FALSE;
-	}
-
-	if (wantAuth == GVNC_AUTH_VENCRYPT_TLSNONE ||
-	    wantAuth ==  GVNC_AUTH_VENCRYPT_TLSPLAIN ||
-	    wantAuth ==  GVNC_AUTH_VENCRYPT_TLSVNC)
+		GVNC_DEBUG("Server refused VeNCrypt auth %d %d\n", gvnc->auth_subtype, status);
+		return FALSE;
+	}
+
+	switch (gvnc->auth_subtype) {
+	case GVNC_AUTH_VENCRYPT_TLSNONE:
+	case GVNC_AUTH_VENCRYPT_TLSPLAIN:
+	case GVNC_AUTH_VENCRYPT_TLSVNC:
 		anonTLS = 1;
-	else
+		break;
+	default:
 		anonTLS = 0;
+	}
 
 	if (!gvnc_start_tls(gvnc, anonTLS)) {
 		GVNC_DEBUG("Could not start TLS\n");
@@ -1325,7 +1486,7 @@ static gboolean gvnc_perform_auth_vencry
 	}
 	GVNC_DEBUG("Completed TLS setup\n");
 
-	switch (wantAuth) {
+	switch (gvnc->auth_subtype) {
 		/* Plain certificate based auth */
 	case GVNC_AUTH_VENCRYPT_TLSNONE:
 	case GVNC_AUTH_VENCRYPT_X509NONE:
@@ -1336,16 +1497,26 @@ static gboolean gvnc_perform_auth_vencry
 	case GVNC_AUTH_VENCRYPT_TLSVNC:
 	case GVNC_AUTH_VENCRYPT_X509VNC:
 		GVNC_DEBUG("Handing off to VNC auth\n");
-		return gvnc_perform_auth_vnc(gvnc, password);
+		return gvnc_perform_auth_vnc(gvnc);
 
 	default:
 		return FALSE;
 	}
 }
 
-static gboolean gvnc_perform_auth(struct gvnc *gvnc, const char *password)
-{
-	int wantAuth = GVNC_AUTH_INVALID;
+static gboolean gvnc_has_auth_type(gpointer data)
+{
+	struct gvnc *gvnc = (struct gvnc *)data;
+
+	if (gvnc->has_error)
+		return TRUE;
+	if (gvnc->auth_type == GVNC_AUTH_INVALID)
+		return FALSE;
+	return TRUE;
+}
+
+static gboolean gvnc_perform_auth(struct gvnc *gvnc)
+{
 	unsigned int nauth, i;
 	unsigned int auth[10];
 
@@ -1372,34 +1543,38 @@ static gboolean gvnc_perform_auth(struct
 		GVNC_DEBUG("Possible auth %d\n", auth[i]);
 	}
 
+	if (gvnc->has_error || !gvnc->ops.auth_type)
+		return FALSE;
+
+	if (!gvnc->ops.auth_type(gvnc->ops_data, nauth, auth))
+		gvnc->has_error = TRUE;
 	if (gvnc->has_error)
 		return FALSE;
 
-	for (i = 0 ; i < nauth ; i++) {
-		if (auth[i] == GVNC_AUTH_NONE ||
-		    auth[i] == GVNC_AUTH_VNC ||
-		    auth[i] == GVNC_AUTH_VENCRYPT) {
-			wantAuth = auth[i];
-			break;
-		}
-	}
+	GVNC_DEBUG("Waiting for auth type\n");
+	g_condition_wait(gvnc_has_auth_type, gvnc);
+	if (gvnc->has_error)
+		return FALSE;
+
+	GVNC_DEBUG("Choose auth %d\n", gvnc->auth_type);
+	if (gvnc_gather_credentials(gvnc))
+		return FALSE;
 
 	if (gvnc->minor > 6) {
-		GVNC_DEBUG("Chose auth %d\n", wantAuth);
-		gvnc_write_u8(gvnc, wantAuth);
+		gvnc_write_u8(gvnc, gvnc->auth_type);
 		gvnc_flush(gvnc);
 	}
 
-	switch (wantAuth) {
+	switch (gvnc->auth_type) {
 	case GVNC_AUTH_NONE:
 		if (gvnc->minor == 8)
 			return gvnc_check_auth_result(gvnc);
 		return TRUE;
 	case GVNC_AUTH_VNC:
-		return gvnc_perform_auth_vnc(gvnc, password);
+		return gvnc_perform_auth_vnc(gvnc);
 
 	case GVNC_AUTH_VENCRYPT:
-		return gvnc_perform_auth_vencrypt(gvnc, password);
+		return gvnc_perform_auth_vencrypt(gvnc);
 
 	default:
 		return FALSE;
@@ -1419,6 +1594,8 @@ struct gvnc *gvnc_new(const struct gvnc_
 
 	memcpy(&gvnc->ops, ops, sizeof(*ops));
 	gvnc->ops_data = ops_data;
+	gvnc->auth_type = GVNC_AUTH_INVALID;
+	gvnc->auth_subtype = GVNC_AUTH_INVALID;
 
 	return gvnc;
 }
@@ -1454,6 +1631,14 @@ void gvnc_close(struct gvnc *gvnc)
 
 	free(gvnc->name);
 	gvnc->name = NULL;
+
+	free(gvnc->cred_username);
+	gvnc->cred_username = NULL;
+	free(gvnc->cred_password);
+	gvnc->cred_password = NULL;
+
+	gvnc->auth_type = GVNC_AUTH_INVALID;
+	gvnc->auth_subtype = GVNC_AUTH_INVALID;
 
 	gvnc->has_error = 0;
 }
@@ -1483,7 +1668,7 @@ gboolean gvnc_is_initialized(struct gvnc
 }
 
 
-gboolean gvnc_initialize(struct gvnc *gvnc, gboolean shared_flag, const char *password)
+gboolean gvnc_initialize(struct gvnc *gvnc, gboolean shared_flag)
 {
 	int ret;
 	char version[13];
@@ -1512,7 +1697,7 @@ gboolean gvnc_initialize(struct gvnc *gv
 	gvnc_flush(gvnc);
 	GVNC_DEBUG("Negotiated protocol %d %d\n", gvnc->major, gvnc->minor);
 
-	if (!gvnc_perform_auth(gvnc, password)) {
+	if (!gvnc_perform_auth(gvnc)) {
 		GVNC_DEBUG("Auth failed\n");
 		goto fail;
 	}
@@ -1570,6 +1755,9 @@ gboolean gvnc_open_host(struct gvnc *gvn
         int ret;
 	if (gvnc_is_open(gvnc))
 		return TRUE;
+
+	gvnc->host = g_strdup(host);
+	gvnc->port = g_strdup(port);
 
         GVNC_DEBUG("Resolving host %s %s\n", host, port);
         memset (&hints, '\0', sizeof (hints));
@@ -1631,6 +1819,66 @@ gboolean gvnc_open_host(struct gvnc *gvn
 }
 
 
+gboolean gvnc_set_auth_type(struct gvnc *gvnc, unsigned int type)
+{
+        GVNC_DEBUG("Requested auth type %d\n", type);
+        if (gvnc->auth_type != GVNC_AUTH_INVALID) {
+                gvnc->has_error = TRUE;
+                return gvnc_has_error(gvnc);
+        }
+        if (type != GVNC_AUTH_NONE &&
+            type != GVNC_AUTH_VNC &&
+            type != GVNC_AUTH_VENCRYPT) {
+                gvnc->has_error = TRUE;
+                return gvnc_has_error(gvnc);
+        }
+        gvnc->auth_type = type;
+        gvnc->auth_subtype = GVNC_AUTH_INVALID;
+
+	return gvnc_has_error(gvnc);
+}
+
+gboolean gvnc_set_auth_subtype(struct gvnc *gvnc, unsigned int type)
+{
+        GVNC_DEBUG("Requested auth subtype %d\n", type);
+        if (gvnc->auth_type != GVNC_AUTH_VENCRYPT) {
+                gvnc->has_error = TRUE;
+		return gvnc_has_error(gvnc);
+        }
+        if (gvnc->auth_subtype != GVNC_AUTH_INVALID) {
+                gvnc->has_error = TRUE;
+		return gvnc_has_error(gvnc);
+        }
+        gvnc->auth_subtype = type;
+
+	return gvnc_has_error(gvnc);
+}
+
+gboolean gvnc_set_credential_password(struct gvnc *gvnc, const char *password)
+{
+        GVNC_DEBUG("Set password credential\n");
+        if (gvnc->cred_password)
+                free(gvnc->cred_password);
+        if (!(gvnc->cred_password = strdup(password))) {
+                gvnc->has_error = TRUE;
+                return FALSE;
+        }
+        return TRUE;
+}
+
+gboolean gvnc_set_credential_username(struct gvnc *gvnc, const char *username)
+{
+        GVNC_DEBUG("Set username credential %s\n", username);
+        if (gvnc->cred_username)
+                free(gvnc->cred_username);
+        if (!(gvnc->cred_username = strdup(username))) {
+                gvnc->has_error = TRUE;
+                return FALSE;
+        }
+        return TRUE;
+}
+
+
 
 gboolean gvnc_set_local(struct gvnc *gvnc, struct gvnc_framebuffer *fb)
 {
diff -r d5a758344261 src/gvnc.h
--- a/src/gvnc.h	Thu Jul 19 21:22:05 2007 -0400
+++ b/src/gvnc.h	Thu Jul 19 21:22:07 2007 -0400
@@ -8,6 +8,9 @@ struct gvnc;
 
 struct gvnc_ops
 {
+	gboolean (*auth_cred)(void *);
+	gboolean (*auth_type)(void *, unsigned int, unsigned int *);
+	gboolean (*auth_subtype)(void *, unsigned int, unsigned int *);
 	gboolean (*update)(void *, int, int, int, int);
 	gboolean (*set_color_map_entry)(void *, int, int, int, int);
 	gboolean (*bell)(void *);
@@ -71,6 +74,29 @@ enum {
 	GVNC_ENCODING_SHARED_MEMORY = -258,
 };
 
+enum {
+	GVNC_AUTH_INVALID = 0,
+	GVNC_AUTH_NONE = 1,
+	GVNC_AUTH_VNC = 2,
+	GVNC_AUTH_RA2 = 5,
+	GVNC_AUTH_RA2NE = 6,
+	GVNC_AUTH_TIGHT = 16,
+	GVNC_AUTH_ULTRA = 17,
+	GVNC_AUTH_TLS = 18,
+	GVNC_AUTH_VENCRYPT = 19
+};
+
+enum {
+	GVNC_AUTH_VENCRYPT_PLAIN = 256,
+	GVNC_AUTH_VENCRYPT_TLSNONE = 257,
+	GVNC_AUTH_VENCRYPT_TLSVNC = 258,
+	GVNC_AUTH_VENCRYPT_TLSPLAIN = 259,
+	GVNC_AUTH_VENCRYPT_X509NONE = 260,
+	GVNC_AUTH_VENCRYPT_X509VNC = 261,
+	GVNC_AUTH_VENCRYPT_X509PLAIN = 262,
+};
+
+
 struct gvnc *gvnc_new(const struct gvnc_ops *ops, gpointer ops_data);
 void gvnc_free(struct gvnc *gvnc);
 
@@ -81,7 +107,15 @@ gboolean gvnc_open_host(struct gvnc *gvn
 gboolean gvnc_open_host(struct gvnc *gvnc, const char *host, const char *port);
 gboolean gvnc_is_open(struct gvnc *gvnc);
 
-gboolean gvnc_initialize(struct gvnc *gvnc, gboolean shared_flag, const char *password);
+gboolean gvnc_set_auth_type(struct gvnc *gvnc, unsigned int type);
+gboolean gvnc_set_auth_subtype(struct gvnc *gvnc, unsigned int type);
+
+gboolean gvnc_set_credential_password(struct gvnc *gvnc, const char *password);
+gboolean gvnc_set_credential_username(struct gvnc *gvnc, const char *username);
+gboolean gvnc_wants_credential_password(struct gvnc *gvnc);
+gboolean gvnc_wants_credential_username(struct gvnc *gvnc);
+
+gboolean gvnc_initialize(struct gvnc *gvnc, gboolean shared_flag);
 gboolean gvnc_is_initialized(struct gvnc *gvnc);
 
 gboolean gvnc_server_message(struct gvnc *gvnc);
diff -r d5a758344261 src/libgtk-vnc_sym.version
--- a/src/libgtk-vnc_sym.version	Thu Jul 19 21:22:05 2007 -0400
+++ b/src/libgtk-vnc_sym.version	Thu Jul 19 21:22:07 2007 -0400
@@ -1,6 +1,8 @@
 {
   global:
     vnc_display_get_type;
+    vnc_display_credential_get_type;
+
     vnc_display_new;
     vnc_display_open_fd;
     vnc_display_open_host;
@@ -9,7 +11,8 @@
 
     vnc_display_send_keys;
 
-    vnc_display_set_password;
+    vnc_display_set_credential;
+
     vnc_display_set_use_shm;
     vnc_display_set_pointer_local;
     vnc_display_set_pointer_grab;
diff -r d5a758344261 src/vncdisplay.c
--- a/src/vncdisplay.c	Thu Jul 19 21:22:05 2007 -0400
+++ b/src/vncdisplay.c	Thu Jul 19 21:22:07 2007 -0400
@@ -42,7 +42,6 @@ struct _VncDisplayPrivate
 	int button_mask;
 	int last_x;
 	int last_y;
-	const char *password;
 
 	gboolean absolute;
 
@@ -63,12 +62,14 @@ enum
 	VNC_CONNECTED,
 	VNC_INITIALIZED,
 	VNC_DISCONNECTED,
+ 	VNC_AUTH_CREDENTIAL,
 
 	LAST_SIGNAL
 };
 
 static guint signals[LAST_SIGNAL] = { 0, 0, 0, 0,
-				      0, 0, 0 };
+				      0, 0, 0, 0 };
+static GParamSpec *signalCredParam;
 
 GtkWidget *vnc_display_new(void)
 {
@@ -479,7 +480,73 @@ static gboolean on_shared_memory_rmid(vo
 	return TRUE;
 }
 
+
+static gboolean on_auth_cred(void *opaque)
+{
+	VncDisplay *obj = VNC_DISPLAY(opaque);
+	GValueArray *credList;
+	GValue username, password;
+
+	memset(&username, 0, sizeof(username));
+	memset(&password, 0, sizeof(password));
+
+	credList = g_value_array_new(2);
+	if (gvnc_wants_credential_username(obj->priv->gvnc)) {
+		g_value_init(&username, G_PARAM_SPEC_VALUE_TYPE(signalCredParam));
+		g_value_set_enum(&username, VNC_DISPLAY_CREDENTIAL_USERNAME);
+		g_value_array_append(credList, &username);
+	}
+	if (gvnc_wants_credential_password(obj->priv->gvnc)) {
+		g_value_init(&password, G_PARAM_SPEC_VALUE_TYPE(signalCredParam));
+		g_value_set_enum(&password, VNC_DISPLAY_CREDENTIAL_PASSWORD);
+		g_value_array_append(credList, &password);
+	}
+
+	g_signal_emit (G_OBJECT (obj),
+		       signals[VNC_AUTH_CREDENTIAL],
+		       0,
+		       credList);
+
+	g_value_array_free(credList);
+
+	return TRUE;
+}
+
+static gboolean on_auth_type(void *opaque, unsigned int ntype, unsigned int *types)
+{
+	VncDisplay *obj = VNC_DISPLAY(opaque);
+	VncDisplayPrivate *priv = obj->priv;
+
+	/*
+	 * XXX lame - we should have some prioritization. That
+	 * said most servers only support 1 auth type at any time
+	 */
+	if (ntype)
+		gvnc_set_auth_type(priv->gvnc, types[0]);
+
+	return TRUE;
+}
+
+static gboolean on_auth_subtype(void *opaque, unsigned int ntype, unsigned int *types)
+{
+	VncDisplay *obj = VNC_DISPLAY(opaque);
+	VncDisplayPrivate *priv = obj->priv;
+
+	/*
+	 * XXX lame - we should have some prioritization. That
+	 * said most servers only support 1 auth type at any time
+	 */
+	if (ntype)
+		gvnc_set_auth_subtype(priv->gvnc, types[0]);
+
+	return TRUE;
+}
+
+
 static const struct gvnc_ops vnc_display_ops = {
+	.auth_cred = on_auth_cred,
+	.auth_type = on_auth_type,
+	.auth_subtype = on_auth_subtype,
 	.update = on_update,
 	.resize = on_resize,
 	.pointer_type_change = on_pointer_type_change,
@@ -514,7 +581,7 @@ static void *vnc_coroutine(void *opaque)
 		       signals[VNC_CONNECTED],
 		       0);
 
-	if (gvnc_initialize(priv->gvnc, FALSE, priv->password))
+	if (gvnc_initialize(priv->gvnc, FALSE))
 		goto cleanup;
 
 	g_signal_emit (G_OBJECT (obj),
@@ -627,6 +694,13 @@ static void vnc_display_class_init(VncDi
 static void vnc_display_class_init(VncDisplayClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	signalCredParam = g_param_spec_enum("credential",
+					    "credential",
+					    "credential",
+					    vnc_display_credential_get_type(),
+					    0,
+					    G_PARAM_READABLE);
 
 	signals[VNC_CONNECTED] =
 		g_signal_new ("vnc-connected",
@@ -657,6 +731,18 @@ static void vnc_display_class_init(VncDi
 			      g_cclosure_marshal_VOID__VOID,
 			      G_TYPE_NONE,
 			      0);
+
+	signals[VNC_AUTH_CREDENTIAL] =
+		g_signal_new ("vnc-auth-credential",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_FIRST,
+			      G_STRUCT_OFFSET (VncDisplayClass, vnc_auth_credential),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__PARAM,
+			      G_TYPE_NONE,
+			      1,
+			      G_TYPE_VALUE_ARRAY);
+
 
 	signals[VNC_POINTER_GRAB] =
 		g_signal_new("vnc-pointer-grab",
@@ -753,9 +839,21 @@ static void vnc_display_init(GTypeInstan
 	display->priv->gvnc = gvnc_new(&vnc_display_ops, obj);
 }
 
-void vnc_display_set_password(VncDisplay *obj, const gchar *password)
-{
-	obj->priv->password = password;
+gboolean vnc_display_set_credential(VncDisplay *obj, int type, const gchar *data)
+{
+	switch (type) {
+	case VNC_DISPLAY_CREDENTIAL_PASSWORD:
+		if (gvnc_set_credential_password(obj->priv->gvnc, data))
+			return FALSE;
+		return TRUE;
+
+	case VNC_DISPLAY_CREDENTIAL_USERNAME:
+		if (gvnc_set_credential_username(obj->priv->gvnc, data))
+			return FALSE;
+		return TRUE;
+	}
+
+	return FALSE;
 }
 
 void vnc_display_set_use_shm(VncDisplay *obj, gboolean enable)
@@ -819,6 +917,23 @@ GType vnc_display_get_type(void)
 
 	return type;
 }
+
+GType vnc_display_credential_get_type(void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			{ VNC_DISPLAY_CREDENTIAL_PASSWORD, "VNC_DISPLAY_CREDENTIAL_PASSWORD", "password" },
+			{ VNC_DISPLAY_CREDENTIAL_USERNAME, "VNC_DISPLAY_CREDENTIAL_USERNAME", "username" },
+			{ 0, NULL, NULL }
+		};
+		etype = g_enum_register_static ("VncDisplayCredentialType", values );
+	}
+
+	return etype;
+}
+
 
 int vnc_display_get_width(VncDisplay *obj)
 {
diff -r d5a758344261 src/vncdisplay.h
--- a/src/vncdisplay.h	Thu Jul 19 21:22:05 2007 -0400
+++ b/src/vncdisplay.h	Thu Jul 19 21:22:07 2007 -0400
@@ -19,6 +19,7 @@ typedef struct _VncDisplayPrivate VncDis
 #include <glib.h>
 
 #define VNC_TYPE_DISPLAY (vnc_display_get_type())
+#define VNC_TYPE_DISPLAY_CREDENTIAL (vnc_display_credential_get_type())
 
 #define VNC_DISPLAY(obj) \
         (G_TYPE_CHECK_INSTANCE_CAST((obj), VNC_TYPE_DISPLAY, VncDisplay))
@@ -50,11 +51,19 @@ struct _VncDisplayClass
 	void		(* vnc_connected)	(VncDisplay *display);
 	void		(* vnc_initialized)	(VncDisplay *display);
 	void		(* vnc_disconnected)	(VncDisplay *display);
+	void		(* vnc_auth_credential)	(VncDisplay *display, GValueArray *credList);
 };
+
+typedef enum
+{
+	VNC_DISPLAY_CREDENTIAL_PASSWORD,
+	VNC_DISPLAY_CREDENTIAL_USERNAME,
+} VncDisplayCredential;
 
 G_BEGIN_DECLS
 
 GType		vnc_display_get_type(void);
+GType		vnc_display_credential_get_type(void);
 GtkWidget *	vnc_display_new(void);
 
 gboolean	vnc_display_open_fd(VncDisplay *obj, int fd);
@@ -64,7 +73,7 @@ void		vnc_display_close(VncDisplay *obj)
 
 void            vnc_display_send_keys(VncDisplay *obj, const guint *keyvals, int nkeyvals);
 
-void		vnc_display_set_password(VncDisplay *obj, const gchar *password);
+gboolean	vnc_display_set_credential(VncDisplay *obj, int type, const gchar *data);
 
 void		vnc_display_set_use_shm(VncDisplay *obj, gboolean enable);
 void		vnc_display_set_pointer_local(VncDisplay *obj, gboolean enable);
diff -r d5a758344261 src/vncmodule.c
--- a/src/vncmodule.c	Thu Jul 19 21:22:05 2007 -0400
+++ b/src/vncmodule.c	Thu Jul 19 21:22:07 2007 -0400
@@ -21,6 +21,7 @@
 #include <pygobject.h>
  
 void gtkvnc_register_classes (PyObject *d); 
+void gtkvnc_add_constants(PyObject *module, const gchar *strip_prefix);
 extern PyMethodDef gtkvnc_functions[];
  
 DL_EXPORT(void) initgtkvnc(void);
@@ -39,6 +40,7 @@ DL_EXPORT(void) initgtkvnc(void)
     if (PyErr_Occurred())
 	Py_FatalError("can't get dict");
  
+    gtkvnc_add_constants(m, "VNC_DISPLAY_");
     gtkvnc_register_classes (d);
  
     if (PyErr_Occurred ()) {


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