PATCH: Add SASL authentication type
- From: "Daniel P. Berrange" <dan berrange com>
- To: gtk-vnc-list gnome org
- Subject: PATCH: Add SASL authentication type
- Date: Mon, 1 Jun 2009 13:01:25 +0100
This is simply a re-post of the previous SASL patch I've sent a few times
before. No noteworthy changes since last time, except for fact that QEMU
now supports this encoding so I'd like to get a GTK-VNC release with this
done.
Daniel
diff -rup gtk-vnc-0.3.8.orig/configure.ac gtk-vnc-0.3.8.new/configure.ac
--- gtk-vnc-0.3.8.orig/configure.ac 2008-12-07 19:35:14.000000000 +0000
+++ gtk-vnc-0.3.8.new/configure.ac 2009-03-03 10:13:25.000000000 +0000
@@ -145,6 +145,53 @@ PKG_CHECK_MODULES(GNUTLS, gnutls >= $GNU
AC_SUBST(GNUTLS_CFLAGS)
AC_SUBST(GNUTLS_LIBS)
+dnl Cyrus SASL
+AC_ARG_WITH([sasl],
+ [ --with-sasl use cyrus SASL for authentication],
+ [],
+ [with_sasl=check])
+
+SASL_CFLAGS=
+SASL_LIBS=
+if test "x$with_sasl" != "xno"; then
+ if test "x$with_sasl" != "xyes" -a "x$with_sasl" != "xcheck"; then
+ SASL_CFLAGS="-I$with_sasl"
+ SASL_LIBS="-L$with_sasl"
+ fi
+ fail=0
+ old_cflags="$CFLAGS"
+ old_libs="$LIBS"
+ CFLAGS="$CFLAGS $SASL_CFLAGS"
+ LIBS="$LIBS $SASL_LIBS"
+ AC_CHECK_HEADER([sasl/sasl.h],[],[
+ if test "x$with_sasl" != "xcheck" ; then
+ with_sasl=no
+ else
+ fail=1
+ fi])
+ if test "x$with_sasl" != "xno" ; then
+ AC_CHECK_LIB([sasl2], [sasl_client_init],[with_sasl=yes],[
+ if test "x$with_sasl" = "xcheck" ; then
+ with_sasl=no
+ else
+ fail=1
+ fi])
+ fi
+ test $fail = 1 &&
+ AC_MSG_ERROR([You must install the Cyrus SASL development package in order to compile GTK-VNC])
+ CFLAGS="$old_cflags"
+ LIBS="$old_libs"
+ SASL_LIBS="$SASL_LIBS -lsasl2"
+ if test "x$with_sasl" = "xyes" ; then
+ AC_DEFINE_UNQUOTED([HAVE_SASL], 1,
+ [whether Cyrus SASL is available for authentication])
+ fi
+fi
+AM_CONDITIONAL([HAVE_SASL], [test "x$with_sasl" = "xyes"])
+AC_SUBST([SASL_CFLAGS])
+AC_SUBST([SASL_LIBS])
+
+
GTHREAD_CFLAGS=
GTHREAD_LIBS=
diff -rup gtk-vnc-0.3.8.orig/examples/gvncviewer.c gtk-vnc-0.3.8.new/examples/gvncviewer.c
--- gtk-vnc-0.3.8.orig/examples/gvncviewer.c 2008-12-07 19:35:14.000000000 +0000
+++ gtk-vnc-0.3.8.new/examples/gvncviewer.c 2009-03-03 10:13:25.000000000 +0000
@@ -241,7 +241,10 @@ static void vnc_credential(GtkWidget *vn
case VNC_DISPLAY_CREDENTIAL_PASSWORD:
data[i] = gtk_entry_get_text(GTK_ENTRY(entry[row]));
break;
+ default:
+ continue;
}
+ row++;
}
}
}
diff -rup gtk-vnc-0.3.8.orig/gtk-vnc.spec.in gtk-vnc-0.3.8.new/gtk-vnc.spec.in
--- gtk-vnc-0.3.8.orig/gtk-vnc.spec.in 2008-12-07 19:35:14.000000000 +0000
+++ gtk-vnc-0.3.8.new/gtk-vnc.spec.in 2009-03-03 10:13:25.000000000 +0000
@@ -14,7 +14,7 @@ Source: http://downloads.sourceforge.net
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
URL: http://gtk-vnc.sf.net/
BuildRequires: gtk2-devel pygtk2-devel python-devel zlib-devel
-BuildRequires: gnutls-devel
+BuildRequires: gnutls-devel cyrus-sasl-devel
%if %{with_plugin}
%if "%{fedora}" > "8"
BuildRequires: xulrunner-devel
diff -rup gtk-vnc-0.3.8.orig/src/gvnc.c gtk-vnc-0.3.8.new/src/gvnc.c
--- gtk-vnc-0.3.8.orig/src/gvnc.c 2008-12-07 19:35:14.000000000 +0000
+++ gtk-vnc-0.3.8.new/src/gvnc.c 2009-03-03 10:13:25.000000000 +0000
@@ -46,6 +46,10 @@
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
+#ifdef HAVE_SASL
+#include <sasl/sasl.h>
+#endif
+
#include <zlib.h>
#include <gdk/gdkkeysyms.h>
@@ -133,6 +137,16 @@ struct gvnc
char *cred_x509_cacrl;
char *cred_x509_cert;
char *cred_x509_key;
+ gboolean want_cred_username;
+ gboolean want_cred_password;
+ gboolean want_cred_x509;
+
+#if HAVE_SASL
+ sasl_conn_t *saslconn; /* SASL context */
+ const char *saslDecoded;
+ unsigned int saslDecodedLength;
+ unsigned int saslDecodedOffset;
+#endif
char read_buffer[4096];
size_t read_offset;
@@ -355,6 +369,133 @@ static int gvnc_zread(struct gvnc *gvnc,
/* IO functions */
+
+/*
+ * Read at least 1 more byte of data straight off the wire
+ * into the requested buffer.
+ */
+static int gvnc_read_wire(struct gvnc *gvnc, void *data, size_t len)
+{
+ int ret;
+
+ reread:
+ if (gvnc->tls_session) {
+ ret = gnutls_read(gvnc->tls_session, data, len);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_AGAIN)
+ errno = EAGAIN;
+ else
+ errno = EIO;
+ ret = -1;
+ }
+ } else
+ ret = recv (gvnc->fd, data, len, 0);
+
+ if (ret == -1) {
+ switch (errno) {
+ case EWOULDBLOCK:
+ if (gvnc->wait_interruptable) {
+ if (!g_io_wait_interruptable(&gvnc->wait,
+ gvnc->channel, G_IO_IN)) {
+ GVNC_DEBUG("Read blocking interrupted %d", gvnc->has_error);
+ return -EAGAIN;
+ }
+ } else
+ g_io_wait(gvnc->channel, G_IO_IN);
+ case EINTR:
+ goto reread;
+
+ default:
+ GVNC_DEBUG("Closing the connection: gvnc_read() - errno=%d\n", errno);
+ gvnc->has_error = TRUE;
+ return -errno;
+ }
+ }
+ if (ret == 0) {
+ GVNC_DEBUG("Closing the connection: gvnc_read() - ret=0\n");
+ gvnc->has_error = TRUE;
+ return -EPIPE;
+ }
+ //GVNC_DEBUG("Read wire %p %d -> %d", data, len, ret);
+
+ return ret;
+}
+
+
+/*
+ * Read at least 1 more byte of data out of the SASL decrypted
+ * data buffer, into the internal read buffer
+ */
+static int gvnc_read_sasl(struct gvnc *gvnc)
+{
+ size_t want;
+ //GVNC_DEBUG("Read SASL %p size %d offset %d", gvnc->saslDecoded,
+ // gvnc->saslDecodedLength, gvnc->saslDecodedOffset);
+ if (gvnc->saslDecoded == NULL) {
+ char encoded[8192];
+ int encodedLen = sizeof(encoded);
+ int err, ret;
+
+ ret = gvnc_read_wire(gvnc, encoded, encodedLen);
+ if (ret < 0) {
+ return ret;
+ }
+
+ err = sasl_decode(gvnc->saslconn, encoded, ret,
+ &gvnc->saslDecoded, &gvnc->saslDecodedLength);
+ if (err != SASL_OK) {
+ GVNC_DEBUG("Failed to decode SASL data %s",
+ sasl_errstring(err, NULL, NULL));
+ gvnc->has_error = TRUE;
+ return -EINVAL;
+ }
+ gvnc->saslDecodedOffset = 0;
+ }
+
+ want = gvnc->saslDecodedLength - gvnc->saslDecodedOffset;
+ if (want > sizeof(gvnc->read_buffer))
+ want = sizeof(gvnc->read_buffer);
+
+ memcpy(gvnc->read_buffer,
+ gvnc->saslDecoded + gvnc->saslDecodedOffset,
+ want);
+ gvnc->saslDecodedOffset += want;
+ if (gvnc->saslDecodedOffset == gvnc->saslDecodedLength) {
+ gvnc->saslDecodedLength = gvnc->saslDecodedOffset = 0;
+ gvnc->saslDecoded = NULL;
+ }
+ //GVNC_DEBUG("Done read write %d - %d", want, gvnc->has_error);
+ return want;
+}
+
+
+/*
+ * Read at least 1 more byte of data straight off the wire
+ * into the internal read buffer
+ */
+static int gvnc_read_plain(struct gvnc *gvnc)
+{
+ //GVNC_DEBUG("Read plain %d", sizeof(gvnc->read_buffer));
+ return gvnc_read_wire(gvnc, gvnc->read_buffer, sizeof(gvnc->read_buffer));
+}
+
+/*
+ * Read at least 1 more byte of data into the internal read_buffer
+ */
+static int gvnc_read_buf(struct gvnc *gvnc)
+{
+ //GVNC_DEBUG("Start read %d", gvnc->has_error);
+#if HAVE_SASL
+ if (gvnc->saslconn)
+ return gvnc_read_sasl(gvnc);
+ else
+#endif
+ return gvnc_read_plain(gvnc);
+}
+
+/*
+ * Fill the 'data' buffer up with exactly 'len' bytes worth of data
+ */
static int gvnc_read(struct gvnc *gvnc, void *data, size_t len)
{
char *ptr = data;
@@ -377,43 +518,10 @@ static int gvnc_read(struct gvnc *gvnc,
offset += ret;
continue;
} else if (gvnc->read_offset == gvnc->read_size) {
- int ret;
-
- if (gvnc->tls_session) {
- ret = gnutls_read(gvnc->tls_session, gvnc->read_buffer, 4096);
- if (ret < 0) {
- if (ret == GNUTLS_E_AGAIN)
- errno = EAGAIN;
- else
- errno = EIO;
- ret = -1;
- }
- } else
- ret = recv (gvnc->fd, gvnc->read_buffer, 4096, 0);
-
- if (ret == -1) {
- switch (errno) {
- case EWOULDBLOCK:
- if (gvnc->wait_interruptable) {
- if (!g_io_wait_interruptable(&gvnc->wait,
- gvnc->channel, G_IO_IN))
- return -EAGAIN;
- } else
- g_io_wait(gvnc->channel, G_IO_IN);
- case EINTR:
- continue;
- default:
- GVNC_DEBUG("Closing the connection: gvnc_read() - errno=%d\n", errno);
- gvnc->has_error = TRUE;
- return -errno;
- }
- }
- if (ret == 0) {
- GVNC_DEBUG("Closing the connection: gvnc_read() - ret=0\n");
- gvnc->has_error = TRUE;
- return -EPIPE;
- }
+ int ret = gvnc_read_buf(gvnc);
+ if (ret < 0)
+ return ret;
gvnc->read_offset = 0;
gvnc->read_size = ret;
}
@@ -429,16 +537,23 @@ static int gvnc_read(struct gvnc *gvnc,
return 0;
}
-static void gvnc_flush(struct gvnc *gvnc)
+/*
+ * Write all 'data' of length 'datalen' bytes out to
+ * the wire
+ */
+static void gvnc_flush_wire(struct gvnc *gvnc,
+ const void *data,
+ size_t datalen)
{
size_t offset = 0;
- while (offset < gvnc->write_offset) {
+ //GVNC_DEBUG("Flush write %p %d", data, datalen);
+ while (offset < datalen) {
int ret;
if (gvnc->tls_session) {
ret = gnutls_write(gvnc->tls_session,
- gvnc->write_buffer+offset,
- gvnc->write_offset-offset);
+ data+offset,
+ datalen-offset);
if (ret < 0) {
if (ret == GNUTLS_E_AGAIN)
errno = EAGAIN;
@@ -448,8 +563,8 @@ static void gvnc_flush(struct gvnc *gvnc
}
} else
ret = send (gvnc->fd,
- gvnc->write_buffer+offset,
- gvnc->write_offset-offset, 0);
+ data+offset,
+ datalen-offset, 0);
if (ret == -1) {
switch (errno) {
case EWOULDBLOCK:
@@ -469,6 +584,57 @@ static void gvnc_flush(struct gvnc *gvnc
}
offset += ret;
}
+}
+
+
+/*
+ * Encode all buffered data, write all encrypted data out
+ * to the wire
+ */
+static void gvnc_flush_sasl(struct gvnc *gvnc)
+{
+ const char *output;
+ unsigned int outputlen;
+ int err;
+
+ err = sasl_encode(gvnc->saslconn,
+ gvnc->write_buffer,
+ gvnc->write_offset,
+ &output, &outputlen);
+ if (err != SASL_OK) {
+ GVNC_DEBUG("Failed to encode SASL data %s",
+ sasl_errstring(err, NULL, NULL));
+ gvnc->has_error = TRUE;
+ return;
+ }
+ //GVNC_DEBUG("Flush SASL %d: %p %d", gvnc->write_offset, output, outputlen);
+ gvnc_flush_wire(gvnc, output, outputlen);
+}
+
+/*
+ * Write all buffered data straight out to the wire
+ */
+static void gvnc_flush_plain(struct gvnc *gvnc)
+{
+ //GVNC_DEBUG("Flush plain %d", gvnc->write_offset);
+ gvnc_flush_wire(gvnc,
+ gvnc->write_buffer,
+ gvnc->write_offset);
+}
+
+
+/*
+ * Write all buffered data out to the wire
+ */
+static void gvnc_flush(struct gvnc *gvnc)
+{
+ //GVNC_DEBUG("STart write %d", gvnc->has_error);
+#if HAVE_SASL
+ if (gvnc->saslconn)
+ gvnc_flush_sasl(gvnc);
+ else
+#endif
+ gvnc_flush_plain(gvnc);
gvnc->write_offset = 0;
}
@@ -602,6 +768,10 @@ static void gvnc_write_s32(struct gvnc *
#define DH_BITS 1024
static gnutls_dh_params_t dh_params;
+static void gvnc_debug_gnutls_log(int level, const char* str) {
+ GVNC_DEBUG("%d %s", level, str);
+}
+
static gboolean gvnc_tls_initialize(void)
{
static int tlsinitialized = 0;
@@ -617,6 +787,11 @@ static gboolean gvnc_tls_initialize(void
if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0)
return FALSE;
+ if (debug_enabled) {
+ //gnutls_global_set_log_level(10);
+ //gnutls_global_set_log_function(gvnc_debug_gnutls_log);
+ }
+
tlsinitialized = TRUE;
return TRUE;
@@ -2120,6 +2295,64 @@ gboolean gvnc_server_message(struct gvnc
return !gvnc_has_error(gvnc);
}
+gboolean gvnc_wants_credential_password(struct gvnc *gvnc)
+{
+ return gvnc->want_cred_password;
+}
+
+gboolean gvnc_wants_credential_username(struct gvnc *gvnc)
+{
+ return gvnc->want_cred_username;
+}
+
+gboolean gvnc_wants_credential_x509(struct gvnc *gvnc)
+{
+ return gvnc->want_cred_x509;
+}
+
+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;
+ /*
+ * For x509 we require a minimum of the CA cert.
+ * Anything else is a bonus - though the server
+ * may reject auth if it decides it wants a client
+ * cert. We can't express that based on auth type
+ * alone though - we'll merely find out when TLS
+ * negotiation takes place.
+ */
+ if (gvnc_wants_credential_x509(gvnc) && !gvnc->cred_x509_cacert)
+ 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 FALSE;
+ }
+ if (!gvnc->ops.auth_cred(gvnc->ops_data))
+ gvnc->has_error = TRUE;
+ if (gvnc->has_error)
+ return FALSE;
+ 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_check_auth_result(struct gvnc *gvnc)
{
uint32_t result;
@@ -2142,7 +2375,7 @@ static gboolean gvnc_check_auth_result(s
if (!gvnc->has_error && gvnc->ops.auth_failure)
gvnc->ops.auth_failure(gvnc->ops_data, reason);
} else {
- GVNC_DEBUG("Fail\n");
+ GVNC_DEBUG("Fail auth no result\n");
if (!gvnc->has_error && gvnc->ops.auth_failure)
gvnc->ops.auth_failure(gvnc->ops_data, NULL);
}
@@ -2155,6 +2388,12 @@ static gboolean gvnc_perform_auth_vnc(st
uint8_t key[8];
GVNC_DEBUG("Do Challenge\n");
+ gvnc->want_cred_password = TRUE;
+ gvnc->want_cred_username = FALSE;
+ gvnc->want_cred_x509 = FALSE;
+ if (!gvnc_gather_credentials(gvnc))
+ return FALSE;
+
if (!gvnc->cred_password)
return FALSE;
@@ -2173,6 +2412,472 @@ static gboolean gvnc_perform_auth_vnc(st
}
+#if HAVE_SASL
+/*
+ * NB, keep in sync with similar method in qemud/remote.c
+ */
+static char *gvnc_addr_to_string(struct sockaddr_storage *sa, socklen_t salen)
+{
+ char host[NI_MAXHOST], port[NI_MAXSERV];
+ char *addr;
+ int err;
+
+ if ((err = getnameinfo((struct sockaddr *)sa, salen,
+ host, sizeof(host),
+ port, sizeof(port),
+ NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
+ GVNC_DEBUG("Cannot resolve address %d: %s",
+ err, gai_strerror(err));
+ return NULL;
+ }
+
+ addr = g_malloc0(strlen(host) + 1 + strlen(port) + 1);
+ strcpy(addr, host);
+ strcat(addr, ";");
+ strcat(addr, port);
+ return addr;
+}
+
+
+
+static gboolean
+gvnc_gather_sasl_credentials(struct gvnc *gvnc,
+ sasl_interact_t *interact)
+{
+ int ninteract;
+
+ gvnc->want_cred_password = FALSE;
+ gvnc->want_cred_username = FALSE;
+ gvnc->want_cred_x509 = FALSE;
+
+ for (ninteract = 0 ; interact[ninteract].id != 0 ; ninteract++) {
+ switch (interact[ninteract].id) {
+ case SASL_CB_AUTHNAME:
+ case SASL_CB_USER:
+ gvnc->want_cred_username = TRUE;
+ break;
+
+ case SASL_CB_PASS:
+ gvnc->want_cred_password = TRUE;
+ break;
+
+ default:
+ GVNC_DEBUG("Unsupported credential %lu",
+ interact[ninteract].id);
+ /* Unsupported */
+ return FALSE;
+ }
+ }
+
+ if ((gvnc->want_cred_password ||
+ gvnc->want_cred_username) &&
+ !gvnc_gather_credentials(gvnc)) {
+ GVNC_DEBUG("%s", "damn ");
+ return FALSE;
+ }
+
+ for (ninteract = 0 ; interact[ninteract].id != 0 ; ninteract++) {
+ switch (interact[ninteract].id) {
+ case SASL_CB_AUTHNAME:
+ case SASL_CB_USER:
+ interact[ninteract].result = gvnc->cred_username;
+ interact[ninteract].len = strlen(gvnc->cred_username);
+ GVNC_DEBUG("Gather Username %s", gvnc->cred_username);
+ break;
+
+ case SASL_CB_PASS:
+ interact[ninteract].result = gvnc->cred_password;
+ interact[ninteract].len = strlen(gvnc->cred_password);
+ GVNC_DEBUG("Gather Password %s", gvnc->cred_password);
+ break;
+ }
+ }
+
+ GVNC_DEBUG("%s", "Filled SASL interact");
+
+ return TRUE;
+}
+
+
+
+/*
+ *
+ * Init msg from server
+ *
+ * u32 mechlist-length
+ * u8-array mechlist-string
+ *
+ * Start msg to server
+ *
+ * u32 mechname-length
+ * u8-array mechname-string
+ * u32 clientout-length
+ * u8-array clientout-string
+ *
+ * Start msg from server
+ *
+ * u32 serverin-length
+ * u8-array serverin-string
+ * u8 continue
+ *
+ * Step msg to server
+ *
+ * u32 clientout-length
+ * u8-array clientout-string
+ *
+ * Step msg from server
+ *
+ * u32 serverin-length
+ * u8-array serverin-string
+ * u8 continue
+ */
+
+#define SASL_MAX_MECHLIST_LEN 300
+#define SASL_MAX_MECHNAME_LEN 100
+#define SASL_MAX_DATA_LEN (1024 * 1024)
+
+/* Perform the SASL authentication process
+ */
+static gboolean gvnc_perform_auth_sasl(struct gvnc *gvnc)
+{
+ sasl_conn_t *saslconn = NULL;
+ sasl_security_properties_t secprops;
+ const char *clientout;
+ char *serverin = NULL;
+ unsigned int clientoutlen, serverinlen;
+ int err, complete;
+ struct sockaddr_storage sa;
+ socklen_t salen;
+ char *localAddr = NULL, *remoteAddr = NULL;
+ const void *val;
+ sasl_ssf_t ssf;
+ sasl_callback_t saslcb[] = {
+ { .id = SASL_CB_AUTHNAME },
+ // { .id = SASL_CB_USER },
+ { .id = SASL_CB_PASS },
+ { .id = 0 },
+ };
+ sasl_interact_t *interact = NULL;
+ guint32 mechlistlen;
+ char *mechlist;
+ const char *mechname;
+ gboolean ret;
+
+ /* Sets up the SASL library as a whole */
+ err = sasl_client_init(NULL);
+ GVNC_DEBUG("Client initialize SASL authentication %d", err);
+ if (err != SASL_OK) {
+ GVNC_DEBUG("failed to initialize SASL library: %d (%s)",
+ err, sasl_errstring(err, NULL, NULL));
+ goto error;
+ }
+
+ /* Get local address in form IPADDR:PORT */
+ salen = sizeof(sa);
+ if (getsockname(gvnc->fd, (struct sockaddr*)&sa, &salen) < 0) {
+ GVNC_DEBUG("failed to get sock address %d (%s)",
+ errno, strerror(errno));
+ goto error;
+ }
+ if ((localAddr = gvnc_addr_to_string(&sa, salen)) == NULL)
+ goto error;
+
+ /* Get remote address in form IPADDR:PORT */
+ salen = sizeof(sa);
+ if (getpeername(gvnc->fd, (struct sockaddr*)&sa, &salen) < 0) {
+ GVNC_DEBUG("failed to get peer address %d (%s)",
+ errno, strerror(errno));
+ g_free(localAddr);
+ goto error;
+ }
+ if ((remoteAddr = gvnc_addr_to_string(&sa, salen)) == NULL) {
+ g_free(localAddr);
+ goto error;
+ }
+
+ GVNC_DEBUG("Client SASL new host:'%s' local:'%s' remote:'%s'", gvnc->host, localAddr, remoteAddr);
+
+ /* Setup a handle for being a client */
+ err = sasl_client_new("vnc",
+ gvnc->host,
+ localAddr,
+ remoteAddr,
+ saslcb,
+ SASL_SUCCESS_DATA,
+ &saslconn);
+ g_free(localAddr);
+ g_free(remoteAddr);
+
+ if (err != SASL_OK) {
+ GVNC_DEBUG("Failed to create SASL client context: %d (%s)",
+ err, sasl_errstring(err, NULL, NULL));
+ goto error;
+ }
+
+ /* Initialize some connection props we care about */
+ if (gvnc->tls_session) {
+ gnutls_cipher_algorithm_t cipher;
+
+ cipher = gnutls_cipher_get(gvnc->tls_session);
+ if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) {
+ GVNC_DEBUG("%s", "invalid cipher size for TLS session");
+ goto error;
+ }
+ ssf *= 8; /* key size is bytes, sasl wants bits */
+
+ GVNC_DEBUG("Setting external SSF %d", ssf);
+ err = sasl_setprop(saslconn, SASL_SSF_EXTERNAL, &ssf);
+ if (err != SASL_OK) {
+ GVNC_DEBUG("cannot set external SSF %d (%s)",
+ err, sasl_errstring(err, NULL, NULL));
+ goto error;
+ }
+ }
+
+ memset (&secprops, 0, sizeof secprops);
+ /* If we've got TLS, we don't care about SSF */
+ secprops.min_ssf = gvnc->tls_session ? 0 : 56; /* Equiv to DES supported by all Kerberos */
+ secprops.max_ssf = gvnc->tls_session ? 0 : 100000; /* Very strong ! AES == 256 */
+ secprops.maxbufsize = 100000;
+ /* If we're not TLS, then forbid any anonymous or trivially crackable auth */
+ secprops.security_flags = gvnc->tls_session ? 0 :
+ SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
+
+ err = sasl_setprop(saslconn, SASL_SEC_PROPS, &secprops);
+ if (err != SASL_OK) {
+ GVNC_DEBUG("cannot set security props %d (%s)",
+ err, sasl_errstring(err, NULL, NULL));
+ goto error;
+ }
+
+ /* Get the supported mechanisms from the server */
+ mechlistlen = gvnc_read_u32(gvnc);
+ if (gvnc->has_error)
+ goto error;
+ if (mechlistlen > SASL_MAX_MECHLIST_LEN) {
+ GVNC_DEBUG("mechlistlen %d too long", mechlistlen);
+ goto error;
+ }
+
+ mechlist = g_malloc(mechlistlen+1);
+ gvnc_read(gvnc, mechlist, mechlistlen);
+ mechlist[mechlistlen] = '\0';
+ if (gvnc->has_error) {
+ g_free(mechlist);
+ mechlist = NULL;
+ goto error;
+ }
+
+#if 0
+ if (wantmech) {
+ if (strstr(mechlist, wantmech) == NULL) {
+ GVNC_DEBUG("SASL mechanism %s not supported by server",
+ wantmech);
+ VIR_FREE(iret.mechlist);
+ goto error;
+ }
+ mechlist = wantmech;
+ }
+#endif
+
+ restart:
+ /* Start the auth negotiation on the client end first */
+ GVNC_DEBUG("Client start negotiation mechlist '%s'", mechlist);
+ err = sasl_client_start(saslconn,
+ mechlist,
+ &interact,
+ &clientout,
+ &clientoutlen,
+ &mechname);
+ if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) {
+ GVNC_DEBUG("Failed to start SASL negotiation: %d (%s)",
+ err, sasl_errdetail(saslconn));
+ g_free(mechlist);
+ mechlist = NULL;
+ goto error;
+ }
+
+ /* Need to gather some credentials from the client */
+ if (err == SASL_INTERACT) {
+ if (!gvnc_gather_sasl_credentials(gvnc,
+ interact)) {
+ GVNC_DEBUG("%s", "Failed to collect auth credentials");
+ goto error;
+ }
+ goto restart;
+ }
+
+ GVNC_DEBUG("Server start negotiation with mech %s. Data %d bytes %p '%s'",
+ mechname, clientoutlen, clientout, clientout);
+
+ if (clientoutlen > SASL_MAX_DATA_LEN) {
+ GVNC_DEBUG("SASL negotiation data too long: %d bytes",
+ clientoutlen);
+ goto error;
+ }
+
+ /* Send back the chosen mechname */
+ gvnc_write_u32(gvnc, strlen(mechname));
+ gvnc_write(gvnc, mechname, strlen(mechname));
+
+ /* NB, distinction of NULL vs "" is *critical* in SASL */
+ if (clientout) {
+ gvnc_write_u32(gvnc, clientoutlen + 1);
+ gvnc_write(gvnc, clientout, clientoutlen + 1);
+ } else {
+ gvnc_write_u32(gvnc, 0);
+ }
+ gvnc_flush(gvnc);
+ if (gvnc->has_error)
+ goto error;
+
+
+ GVNC_DEBUG("%s", "Getting sever start negotiation reply");
+ /* Read the 'START' message reply from server */
+ serverinlen = gvnc_read_u32(gvnc);
+ if (gvnc->has_error)
+ goto error;
+ if (serverinlen > SASL_MAX_DATA_LEN) {
+ GVNC_DEBUG("SASL negotiation data too long: %d bytes",
+ clientoutlen);
+ goto error;
+ }
+
+ /* NB, distinction of NULL vs "" is *critical* in SASL */
+ if (serverinlen) {
+ serverin = g_malloc(serverinlen);
+ gvnc_read(gvnc, serverin, serverinlen);
+ serverin[serverinlen-1] = '\0';
+ serverinlen--;
+ } else {
+ serverin = NULL;
+ }
+ complete = gvnc_read_u8(gvnc);
+ if (gvnc->has_error)
+ goto error;
+
+ GVNC_DEBUG("Client start result complete: %d. Data %d bytes %p '%s'",
+ complete, serverinlen, serverin, serverin);
+
+ /* Loop-the-loop...
+ * Even if the server has completed, the client must *always* do at least one step
+ * in this loop to verify the server isn't lying about something. Mutual auth */
+ for (;;) {
+ restep:
+ err = sasl_client_step(saslconn,
+ serverin,
+ serverinlen,
+ &interact,
+ &clientout,
+ &clientoutlen);
+ if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) {
+ GVNC_DEBUG("Failed SASL step: %d (%s)",
+ err, sasl_errdetail(saslconn));
+ goto error;
+ }
+
+ /* Need to gather some credentials from the client */
+ if (err == SASL_INTERACT) {
+ if (!gvnc_gather_sasl_credentials(gvnc,
+ interact)) {
+ GVNC_DEBUG("%s", "Failed to collect auth credentials");
+ goto error;
+ }
+ goto restep;
+ }
+
+ if (serverin) {
+ g_free(serverin);
+ serverin = NULL;
+ }
+
+ GVNC_DEBUG("Client step result %d. Data %d bytes %p '%s'", err, clientoutlen, clientout, clientout);
+
+ /* Previous server call showed completion & we're now locally complete too */
+ if (complete && err == SASL_OK)
+ break;
+
+ /* Not done, prepare to talk with the server for another iteration */
+
+ /* NB, distinction of NULL vs "" is *critical* in SASL */
+ if (clientout) {
+ gvnc_write_u32(gvnc, clientoutlen + 1);
+ gvnc_write(gvnc, clientout, clientoutlen + 1);
+ } else {
+ gvnc_write_u32(gvnc, 0);
+ }
+ gvnc_flush(gvnc);
+ if (gvnc->has_error)
+ goto error;
+
+ GVNC_DEBUG("Server step with %d bytes %p", clientoutlen, clientout);
+
+ serverinlen = gvnc_read_u32(gvnc);
+ if (gvnc->has_error)
+ goto error;
+ if (serverinlen > SASL_MAX_DATA_LEN) {
+ GVNC_DEBUG("SASL negotiation data too long: %d bytes",
+ clientoutlen);
+ goto error;
+ }
+
+ /* NB, distinction of NULL vs "" is *critical* in SASL */
+ if (serverinlen) {
+ serverin = g_malloc(serverinlen);
+ gvnc_read(gvnc, serverin, serverinlen);
+ serverin[serverinlen-1] = '\0';
+ serverinlen--;
+ } else {
+ serverin = NULL;
+ }
+ complete = gvnc_read_u8(gvnc);
+ if (gvnc->has_error)
+ goto error;
+
+ GVNC_DEBUG("Client step result complete: %d. Data %d bytes %p '%s'",
+ complete, serverinlen, serverin, serverin);
+
+ /* This server call shows complete, and earlier client step was OK */
+ if (complete && err == SASL_OK) {
+ g_free(serverin);
+ serverin = NULL;
+ break;
+ }
+ }
+
+ /* Check for suitable SSF if non-TLS */
+ if (!gvnc->tls_session) {
+ err = sasl_getprop(saslconn, SASL_SSF, &val);
+ if (err != SASL_OK) {
+ GVNC_DEBUG("cannot query SASL ssf on connection %d (%s)",
+ err, sasl_errstring(err, NULL, NULL));
+ goto error;
+ }
+ ssf = *(const int *)val;
+ GVNC_DEBUG("SASL SSF value %d", ssf);
+ if (ssf < 56) { /* 56 == DES level, good for Kerberos */
+ GVNC_DEBUG("negotiation SSF %d was not strong enough", ssf);
+ goto error;
+ }
+ }
+
+ GVNC_DEBUG("%s", "SASL authentication complete");
+ ret = gvnc_check_auth_result(gvnc);
+ /* This must come *after* check-auth-result, because the former
+ * is defined to be sent unencrypted, and setting saslconn turns
+ * on the SSF layer encryption processing */
+ gvnc->saslconn = saslconn;
+ return ret;
+
+ error:
+ gvnc->has_error = TRUE;
+ if (saslconn)
+ sasl_dispose(&saslconn);
+ return FALSE;
+}
+#endif /* HAVE_SASL */
+
+
static gboolean gvnc_start_tls(struct gvnc *gvnc, int anonTLS)
{
static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
@@ -2230,6 +2935,12 @@ static gboolean gvnc_start_tls(struct gv
return FALSE;
}
} else {
+ gvnc->want_cred_password = FALSE;
+ gvnc->want_cred_username = FALSE;
+ gvnc->want_cred_x509 = TRUE;
+ if (!gvnc_gather_credentials(gvnc))
+ return FALSE;
+
gnutls_certificate_credentials_t x509_cred = gvnc_tls_initialize_cert_cred(gvnc);
if (!x509_cred) {
gnutls_deinit(gvnc->tls_session);
@@ -2279,92 +2990,6 @@ static gboolean gvnc_start_tls(struct gv
}
}
-gboolean gvnc_wants_credential_password(struct gvnc *gvnc)
-{
- if (gvnc->auth_type == GVNC_AUTH_VNC)
- return TRUE;
-
- if (gvnc->auth_type == GVNC_AUTH_TLS &&
- gvnc->auth_subtype == 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;
-}
-
-gboolean gvnc_wants_credential_x509(struct gvnc *gvnc)
-{
- if (gvnc->auth_type == GVNC_AUTH_VENCRYPT) {
- if (gvnc->auth_subtype == GVNC_AUTH_VENCRYPT_X509NONE ||
- gvnc->auth_subtype == GVNC_AUTH_VENCRYPT_X509PLAIN ||
- gvnc->auth_subtype == GVNC_AUTH_VENCRYPT_X509VNC)
- 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;
- /*
- * For x509 we require a minimum of the CA cert.
- * Anything else is a bonus - though the server
- * may reject auth if it decides it wants a client
- * cert. We can't express that based on auth type
- * alone though - we'll merely find out when TLS
- * negotiation takes place.
- */
- if (gvnc_wants_credential_x509(gvnc) && !gvnc->cred_x509_cacert)
- 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)
{
@@ -2387,14 +3012,18 @@ static gboolean gvnc_perform_auth_tls(st
GVNC_DEBUG("Could not start TLS\n");
return FALSE;
}
- GVNC_DEBUG("Completed TLS setup\n");
+ GVNC_DEBUG("Completed TLS setup!!!\n");
nauth = gvnc_read_u8(gvnc);
+ GVNC_DEBUG("Got %d subauths\n", nauth);
if (gvnc_has_error(gvnc))
return FALSE;
- if (nauth == 0)
+ GVNC_DEBUG("Got %d subauths\n", nauth);
+ if (nauth == 0) {
+ GVNC_DEBUG("No sub-auth types requested\n");
return gvnc_check_auth_result(gvnc);
+ }
if (nauth > sizeof(auth)) {
GVNC_DEBUG("Too many (%d) auth types\n", nauth);
@@ -2424,9 +3053,6 @@ static gboolean gvnc_perform_auth_tls(st
GVNC_DEBUG("Choose auth %d\n", gvnc->auth_subtype);
- if (!gvnc_gather_credentials(gvnc))
- return FALSE;
-
gvnc_write_u8(gvnc, gvnc->auth_subtype);
gvnc_flush(gvnc);
@@ -2437,6 +3063,10 @@ static gboolean gvnc_perform_auth_tls(st
return TRUE;
case GVNC_AUTH_VNC:
return gvnc_perform_auth_vnc(gvnc);
+#ifdef HAVE_SASL
+ case GVNC_AUTH_SASL:
+ return gvnc_perform_auth_sasl(gvnc);
+#endif
default:
return FALSE;
}
@@ -2519,6 +3149,7 @@ static gboolean gvnc_perform_auth_vencry
case GVNC_AUTH_VENCRYPT_TLSNONE:
case GVNC_AUTH_VENCRYPT_TLSPLAIN:
case GVNC_AUTH_VENCRYPT_TLSVNC:
+ case GVNC_AUTH_VENCRYPT_TLSSASL:
anonTLS = 1;
break;
default:
@@ -2529,7 +3160,7 @@ static gboolean gvnc_perform_auth_vencry
GVNC_DEBUG("Could not start TLS\n");
return FALSE;
}
- GVNC_DEBUG("Completed TLS setup\n");
+ GVNC_DEBUG("Completed TLS setup, do subauth %d\n", gvnc->auth_subtype);
switch (gvnc->auth_subtype) {
/* Plain certificate based auth */
@@ -2544,7 +3175,16 @@ static gboolean gvnc_perform_auth_vencry
GVNC_DEBUG("Handing off to VNC auth\n");
return gvnc_perform_auth_vnc(gvnc);
+#ifdef HAVE_SASL
+ /* SASL layered over TLS */
+ case GVNC_AUTH_VENCRYPT_TLSSASL:
+ case GVNC_AUTH_VENCRYPT_X509SASL:
+ GVNC_DEBUG("Handing off to SASL auth\n");
+ return gvnc_perform_auth_sasl(gvnc);
+#endif
+
default:
+ GVNC_DEBUG("Unknown auth subtype %d\n", gvnc->auth_subtype);
return FALSE;
}
}
@@ -2626,6 +3266,11 @@ static gboolean gvnc_perform_auth(struct
case GVNC_AUTH_VENCRYPT:
return gvnc_perform_auth_vencrypt(gvnc);
+#ifdef HAVE_SASL
+ case GVNC_AUTH_SASL:
+ return gvnc_perform_auth_sasl(gvnc);
+#endif
+
default:
if (gvnc->ops.auth_unsupported)
gvnc->ops.auth_unsupported (gvnc->ops_data, gvnc->auth_type);
@@ -2671,6 +3316,11 @@ void gvnc_close(struct gvnc *gvnc)
gnutls_bye(gvnc->tls_session, GNUTLS_SHUT_RDWR);
gvnc->tls_session = NULL;
}
+#if HAVE_SASL
+ if (gvnc->saslconn)
+ sasl_dispose (&gvnc->saslconn);
+#endif
+
if (gvnc->channel) {
g_io_channel_unref(gvnc->channel);
gvnc->channel = NULL;
@@ -2987,7 +3637,7 @@ gboolean gvnc_open_host(struct gvnc *gvn
gboolean gvnc_set_auth_type(struct gvnc *gvnc, unsigned int type)
{
- GVNC_DEBUG("Requested auth type %u\n", type);
+ GVNC_DEBUG("Thinking about auth type %u", type);
if (gvnc->auth_type != GVNC_AUTH_INVALID) {
gvnc->has_error = TRUE;
return !gvnc_has_error(gvnc);
@@ -2995,13 +3645,16 @@ gboolean gvnc_set_auth_type(struct gvnc
if (type != GVNC_AUTH_NONE &&
type != GVNC_AUTH_VNC &&
type != GVNC_AUTH_TLS &&
- type != GVNC_AUTH_VENCRYPT) {
+ type != GVNC_AUTH_VENCRYPT &&
+ type != GVNC_AUTH_SASL) {
+ GVNC_DEBUG("Unsupported auth type %u", type);
if (gvnc->ops.auth_unsupported)
- gvnc->ops.auth_unsupported (gvnc->ops_data, type);
+ gvnc->ops.auth_unsupported (gvnc->ops_data, type);
gvnc->has_error = TRUE;
return !gvnc_has_error(gvnc);
}
+ GVNC_DEBUG("Decided on auth type %u", type);
gvnc->auth_type = type;
gvnc->auth_subtype = GVNC_AUTH_INVALID;
@@ -3027,7 +3680,7 @@ gboolean gvnc_set_auth_subtype(struct gv
gboolean gvnc_set_credential_password(struct gvnc *gvnc, const char *password)
{
- GVNC_DEBUG("Set password credential\n");
+ GVNC_DEBUG("Set password credential %s", password);
if (gvnc->cred_password)
g_free(gvnc->cred_password);
if (!(gvnc->cred_password = g_strdup(password))) {
@@ -3039,7 +3692,7 @@ gboolean gvnc_set_credential_password(st
gboolean gvnc_set_credential_username(struct gvnc *gvnc, const char *username)
{
- GVNC_DEBUG("Set username credential %s\n", username);
+ GVNC_DEBUG("Set username credential %s", username);
if (gvnc->cred_username)
g_free(gvnc->cred_username);
if (!(gvnc->cred_username = g_strdup(username))) {
@@ -3051,7 +3704,7 @@ gboolean gvnc_set_credential_username(st
gboolean gvnc_set_credential_x509_cacert(struct gvnc *gvnc, const char *file)
{
- GVNC_DEBUG("Set x509 cacert %s\n", file);
+ GVNC_DEBUG("Set x509 cacert %s", file);
if (gvnc->cred_x509_cacert)
g_free(gvnc->cred_x509_cacert);
if (!(gvnc->cred_x509_cacert = g_strdup(file))) {
diff -rup gtk-vnc-0.3.8.orig/src/gvnc.h gtk-vnc-0.3.8.new/src/gvnc.h
--- gtk-vnc-0.3.8.orig/src/gvnc.h 2008-12-07 19:35:14.000000000 +0000
+++ gtk-vnc-0.3.8.new/src/gvnc.h 2009-03-03 10:13:25.000000000 +0000
@@ -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_SASL = 20, /* SASL type used by VINO and QEMU */
} gvnc_auth;
typedef enum {
@@ -139,6 +140,8 @@ typedef enum {
GVNC_AUTH_VENCRYPT_X509NONE = 260,
GVNC_AUTH_VENCRYPT_X509VNC = 261,
GVNC_AUTH_VENCRYPT_X509PLAIN = 262,
+ GVNC_AUTH_VENCRYPT_X509SASL = 263,
+ GVNC_AUTH_VENCRYPT_TLSSASL = 264,
} gvnc_auth_vencrypt;
diff -rup gtk-vnc-0.3.8.orig/src/Makefile.am gtk-vnc-0.3.8.new/src/Makefile.am
--- gtk-vnc-0.3.8.orig/src/Makefile.am 2008-12-07 19:35:14.000000000 +0000
+++ gtk-vnc-0.3.8.new/src/Makefile.am 2009-03-03 10:13:25.000000000 +0000
@@ -4,10 +4,10 @@ EXTRA_DIST = libgtk-vnc_sym.version vncm
lib_LTLIBRARIES = libgtk-vnc-1.0.la
libgtk_vnc_1_0_la_LIBADD = @GTK_LIBS@ @GNUTLS_LIBS@ \
- @GTHREAD_LIBS@ \
+ @GTHREAD_LIBS@ @SASL_LIBS@ \
../gnulib/lib/libgnu.la
libgtk_vnc_1_0_la_CFLAGS = @GTK_CFLAGS@ @GNUTLS_CFLAGS@ \
- @GTHREAD_CFLAGS@ @WARNING_CFLAGS@ \
+ @GTHREAD_CFLAGS@ @SASL_CFLAGS@ @WARNING_CFLAGS@ \
-DSYSCONFDIR=\""$(sysconfdir)"\" \
-DG_LOG_DOMAIN=\"gtk-vnc\" \
-I$(top_srcdir)/gnulib/lib -I../gnulib/lib
diff -rup gtk-vnc-0.3.8.orig/src/vncdisplay.c gtk-vnc-0.3.8.new/src/vncdisplay.c
--- gtk-vnc-0.3.8.orig/src/vncdisplay.c 2009-03-03 10:13:12.000000000 +0000
+++ gtk-vnc-0.3.8.new/src/vncdisplay.c 2009-03-03 10:13:25.000000000 +0000
@@ -1904,9 +1904,23 @@ static void vnc_display_init(VncDisplay
priv->shared_flag = FALSE;
priv->force_size = TRUE;
+ /*
+ * Both these two provide TLS based auth, and can layer
+ * all the other auth types on top. So these two must
+ * be the first listed
+ */
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));
+
+ /*
+ * Then stackable auth types in order of preference
+ */
+ priv->preferable_auths = g_slist_append (priv->preferable_auths, GUINT_TO_POINTER (GVNC_AUTH_SASL));
priv->preferable_auths = g_slist_append (priv->preferable_auths, GUINT_TO_POINTER (GVNC_AUTH_VNC));
+
+ /*
+ * Or nothing at all
+ */
priv->preferable_auths = g_slist_append (priv->preferable_auths, GUINT_TO_POINTER (GVNC_AUTH_NONE));
priv->gvnc = gvnc_new(&vnc_display_ops, obj);
--
|: http://berrange.com/ -o- http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: http://freshmeat.net/~danielpb/ -o- http://gtk-vnc.sourceforge.net :|
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]