1302 lines
36 KiB
Diff
1302 lines
36 KiB
Diff
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);
|