|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH RFC 5/6] qemu-xen-trad: sasl: introduce SASL authentication and encryption layer
This change adds calls to the SASL API to negotiate SASL auth and
includes SASL encode/decode into read and write flows if the SASL
mechanism is providing SSF.
The code is taken from upstream with minor adjustments for
compatibility with qemu-xen-traditional.
Signed-off-by: Simon Waterman <watermansrdev@xxxxxxxxx>
---
vnc.c | 329 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 292 insertions(+), 37 deletions(-)
diff --git a/vnc.c b/vnc.c
index 728efec..ff460b8 100644
--- a/vnc.c
+++ b/vnc.c
@@ -80,6 +80,58 @@ static DisplayChangeListener *dcl;
(((x) + (1ULL << (vs)->dirty_pixel_shift) - 1) >> (vs)->dirty_pixel_shift)
#define DP2X(vs, x) ((x) << (vs)->dirty_pixel_shift)
+#ifndef CONFIG_STUBDOM
+static char *addr_to_string(const char *format,
+ struct sockaddr_storage *sa,
+ socklen_t salen) {
+ char *addr;
+ char host[NI_MAXHOST];
+ char serv[NI_MAXSERV];
+ int err;
+ size_t addrlen;
+
+ if ((err = getnameinfo((struct sockaddr *)sa, salen,
+ host, sizeof(host),
+ serv, sizeof(serv),
+ NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
+ VNC_DEBUG("Cannot resolve address %d: %s\n",
+ err, gai_strerror(err));
+ return NULL;
+ }
+
+ /* Enough for the existing format + the 2 vars we're
+ * substituting in. */
+ addrlen = strlen(format) + strlen(host) + strlen(serv);
+ addr = malloc(addrlen + 1);
+ snprintf(addr, addrlen, format, host, serv);
+ addr[addrlen] = '\0';
+
+ return addr;
+}
+
+char *vnc_socket_local_addr(const char *format, int fd) {
+ struct sockaddr_storage sa;
+ socklen_t salen;
+
+ salen = sizeof(sa);
+ if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0)
+ return NULL;
+
+ return addr_to_string(format, &sa, salen);
+}
+
+char *vnc_socket_remote_addr(const char *format, int fd) {
+ struct sockaddr_storage sa;
+ socklen_t salen;
+
+ salen = sizeof(sa);
+ if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0)
+ return NULL;
+
+ return addr_to_string(format, &sa, salen);
+}
+#endif /* !CONFIG_STUBDOM */
+
void do_info_vnc(void)
{
if (vnc_state == NULL)
@@ -770,6 +822,9 @@ int vnc_client_io_error(VncState *vs, int ret, int
last_errno)
}
vs->wiremode = VNC_WIREMODE_CLEAR;
#endif /* CONFIG_VNC_TLS */
+#ifdef CONFIG_VNC_SASL
+ vnc_sasl_client_cleanup(vs);
+#endif /* CONFIG_VNC_SASL */
return 0;
}
return ret;
@@ -780,65 +835,203 @@ void vnc_client_error(VncState *vs)
vnc_client_io_error(vs, -1, EINVAL);
}
-static void vnc_client_write(void *opaque)
+#ifdef CONFIG_VNC_TLS
+static long vnc_client_write_tls(gnutls_session_t *session,
+ const uint8_t *data,
+ size_t datalen)
+{
+ long ret = gnutls_write(*session, data, datalen);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_AGAIN) {
+ errno = EAGAIN;
+ } else {
+ errno = EIO;
+ }
+ ret = -1;
+ }
+ return ret;
+}
+#endif /* CONFIG_VNC_TLS */
+
+/*
+ * Called to write a chunk of data to the client socket. The data may
+ * be the raw data, or may have already been encoded by SASL.
+ * The data will be written either straight onto the socket, or
+ * written via the GNUTLS wrappers, if TLS/SSL encryption is enabled
+ *
+ * NB, it is theoretically possible to have 2 layers of encryption,
+ * both SASL, and this TLS layer. It is highly unlikely in practice
+ * though, since SASL encryption will typically be a no-op if TLS
+ * is active
+ *
+ * Returns the number of bytes written, which may be less than
+ * the requested 'datalen' if the socket would block. Returns
+ * -1 on error, and disconnects the client socket.
+ */
+long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
{
long ret;
- VncState *vs = opaque;
-
#ifdef CONFIG_VNC_TLS
if (vs->tls_session) {
- ret = gnutls_write(vs->tls_session, vs->output.buffer,
vs->output.offset);
- if (ret < 0) {
- if (ret == GNUTLS_E_AGAIN)
- errno = EAGAIN;
- else
- errno = EIO;
- ret = -1;
- }
- } else
+ ret = vnc_client_write_tls(&vs->tls_session, data, datalen);
+ } else {
+#endif /* CONFIG_VNC_TLS */
+ ret = send(vs->csock, data, datalen, 0);
+#ifdef CONFIG_VNC_TLS
+ }
#endif /* CONFIG_VNC_TLS */
- ret = send(vs->csock, vs->output.buffer, vs->output.offset, 0);
- ret = vnc_client_io_error(vs, ret, socket_error());
+ VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret);
+ return vnc_client_io_error(vs, ret, socket_error());
+}
+
+/*
+ * Called to write buffered data to the client socket, when not
+ * using any SASL SSF encryption layers. Will write as much data
+ * as possible without blocking. If all buffered data is written,
+ * will switch the FD poll() handler back to read monitoring.
+ *
+ * Returns the number of bytes written, which may be less than
+ * the buffered output data if the socket would block. Returns
+ * -1 on error, and disconnects the client socket.
+ */
+static long vnc_client_write_plain(VncState *vs)
+{
+ long ret;
+
+#ifdef CONFIG_VNC_SASL
+ VNC_DEBUG("Write Plain: Pending output %p size %zd offset %zd. Wait SSF
%d\n",
+ vs->output.buffer, vs->output.capacity, vs->output.offset,
+ vs->sasl.waitWriteSSF);
+
+ if (vs->sasl.conn &&
+ vs->sasl.runSSF &&
+ vs->sasl.waitWriteSSF) {
+ ret = vnc_client_write_buf(vs, vs->output.buffer,
vs->sasl.waitWriteSSF);
+ if (ret)
+ vs->sasl.waitWriteSSF -= ret;
+ } else
+#endif /* CONFIG_VNC_SASL */
+ ret = vnc_client_write_buf(vs, vs->output.buffer, vs->output.offset);
if (!ret)
- return;
+ return 0;
buffer_advance(&vs->output, ret);
- if (vs->output.offset == 0)
- qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+ if (vs->output.offset == 0) {
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+ }
+
+ return ret;
}
-static void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
+/*
+ * First function called whenever there is data to be written to
+ * the client socket. Will delegate actual work according to whether
+ * SASL SSF layers are enabled (thus requiring encryption calls)
+ */
+static void vnc_client_write(void *opaque)
+{
+ long ret;
+ VncState *vs = opaque;
+
+#ifdef CONFIG_VNC_SASL
+ if (vs->sasl.conn &&
+ vs->sasl.runSSF &&
+ !vs->sasl.waitWriteSSF)
+ ret = vnc_client_write_sasl(vs);
+ else
+#endif /* CONFIG_VNC_SASL */
+ ret = vnc_client_write_plain(vs);
+}
+
+void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
{
vs->read_handler = func;
vs->read_handler_expect = expecting;
}
-void vnc_client_read(void *opaque)
+#ifdef CONFIG_VNC_TLS
+static long vnc_client_read_tls(gnutls_session_t *session, uint8_t *data,
+ size_t datalen)
{
- VncState *vs = opaque;
- long ret;
-
- buffer_reserve(&vs->input, 4096);
+ long ret = gnutls_read(*session, data, datalen);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_AGAIN) {
+ errno = EAGAIN;
+ } else {
+ errno = EIO;
+ }
+ ret = -1;
+ }
+ return ret;
+}
+#endif /* CONFIG_VNC_TLS */
+/*
+ * Called to read a chunk of data from the client socket. The data may
+ * be the raw data, or may need to be further decoded by SASL.
+ * The data will be read either straight from to the socket, or
+ * read via the GNUTLS wrappers, if TLS/SSL encryption is enabled
+ *
+ * NB, it is theoretically possible to have 2 layers of encryption,
+ * both SASL, and this TLS layer. It is highly unlikely in practice
+ * though, since SASL encryption will typically be a no-op if TLS
+ * is active
+ *
+ * Returns the number of bytes read, which may be less than
+ * the requested 'datalen' if the socket would block. Returns
+ * -1 on error, and disconnects the client socket.
+ */
+long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
+{
+ long ret;
#ifdef CONFIG_VNC_TLS
if (vs->tls_session) {
- ret = gnutls_read(vs->tls_session, buffer_end(&vs->input), 4096);
- if (ret < 0) {
- if (ret == GNUTLS_E_AGAIN)
- errno = EAGAIN;
- else
- errno = EIO;
- ret = -1;
- }
- } else
+ ret = vnc_client_read_tls(&vs->tls_session, data, datalen);
+ } else {
#endif /* CONFIG_VNC_TLS */
- ret = recv(vs->csock, buffer_end(&vs->input), 4096, 0);
- ret = vnc_client_io_error(vs, ret, socket_error());
- if (!ret)
- return;
+ ret = recv(vs->csock, data, datalen, 0);
+#ifdef CONFIG_VNC_TLS
+ }
+#endif /* CONFIG_VNC_TLS */
+ VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret);
+ return vnc_client_io_error(vs, ret, socket_error());
+}
+/*
+ * Called to read data from the client socket to the input buffer,
+ * when not using any SASL SSF encryption layers. Will read as much
+ * data as possible without blocking.
+ *
+ * Returns the number of bytes read. Returns -1 on error, and
+ * disconnects the client socket.
+ */
+static long vnc_client_read_plain(VncState *vs)
+{
+ int ret;
+ VNC_DEBUG("Read plain %p size %zd offset %zd\n",
+ vs->input.buffer, vs->input.capacity, vs->input.offset);
+ buffer_reserve(&vs->input, 4096);
+ ret = vnc_client_read_buf(vs, buffer_end(&vs->input), 4096);
+ if (!ret)
+ return 0;
vs->input.offset += ret;
+ return ret;
+}
+
+void vnc_client_read(void *opaque)
+{
+ VncState *vs = opaque;
+ long ret;
+
+#ifdef CONFIG_VNC_SASL
+ if (vs->sasl.conn && vs->sasl.runSSF)
+ ret = vnc_client_read_sasl(vs);
+ else
+#endif /* CONFIG_VNC_SASL */
+ ret = vnc_client_read_plain(vs);
+ if (!ret)
+ return;
while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) {
size_t len = vs->read_handler_expect;
@@ -1897,6 +2090,14 @@ static int start_auth_vencrypt_subauth(VncState *vs)
VNC_DEBUG("Start TLS auth VNC\n");
return start_auth_vnc(vs);
+#ifdef CONFIG_VNC_SASL
+ case VNC_AUTH_VENCRYPT_X509SASL:
+ case VNC_AUTH_VENCRYPT_TLSSASL:
+ VNC_DEBUG("Start TLS auth SASL\n");
+ start_auth_sasl(vs);
+ break;
+#endif /* CONFIG_VNC_SASL */
+
default: /* Should not be possible, but just in case */
VNC_DEBUG("Reject auth %d\n", vs->auth);
vnc_write_u8(vs, 1);
@@ -1957,7 +2158,9 @@ static void vnc_handshake_io(void *opaque) {
#define NEED_X509_AUTH(vs) \
((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE || \
(vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC || \
- (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN)
+ (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \
+ (vs)->subauth == VNC_AUTH_VENCRYPT_X509SASL || \
+ (vs)->subauth == VNC_AUTH_VENCRYPT_TLSSASL)
#if defined(GNUTLS_VERSION_NUMBER) && \
GNUTLS_VERSION_NUMBER >= 0x020200 /* 2.2.0 */
@@ -2172,6 +2375,13 @@ static int protocol_client_auth(VncState *vs, uint8_t
*data, size_t len)
return start_auth_vencrypt(vs);
#endif /* CONFIG_VNC_TLS */
+#ifdef CONFIG_VNC_SASL
+ case VNC_AUTH_SASL:
+ VNC_DEBUG("Accept SASL auth\n");
+ start_auth_sasl(vs);
+ break;
+#endif /* CONFIG_VNC_SASL */
+
default: /* Should not be possible, but just in case */
VNC_DEBUG("Reject auth %d\n", vs->auth);
vnc_write_u8(vs, 1);
@@ -2435,6 +2645,10 @@ int vnc_display_open(DisplayState *ds, const char
*display, int find_unused)
#ifdef CONFIG_VNC_TLS
int tls = 0, x509 = 0;
#endif
+ bool sasl = false;
+#ifdef CONFIG_VNC_SASL
+ int saslErr;
+#endif
if (display == NULL)
display = "localhost:0";
@@ -2453,6 +2667,15 @@ int vnc_display_open(DisplayState *ds, const char
*display, int find_unused)
password = 1; /* Require password auth */
} else if (strncmp(options, "switchbpp", 9) == 0) {
vs->switchbpp = 1;
+ } else if (strncmp(options, "sasl", 4) == 0) {
+#ifdef CONFIG_VNC_SASL
+ sasl = 1; /* Require SASL auth */
+#else
+ fprintf(stderr, "VNC SASL auth requires cyrus-sasl support\n");
+ qemu_free(vs->display);
+ vs->display = NULL;
+ return -1;
+#endif /* CONFIG_VNC_SASL */
#ifdef CONFIG_VNC_TLS
} else if (strncmp(options, "tls", 3) == 0) {
tls = 1; /* Require TLS */
@@ -2509,6 +2732,27 @@ int vnc_display_open(DisplayState *ds, const char
*display, int find_unused)
vs->subauth = VNC_AUTH_INVALID;
}
#endif
+#ifdef CONFIG_VNC_SASL
+ } else if (sasl) {
+#ifdef CONFIG_VNC_TLS
+ if (tls) {
+ vs->auth = VNC_AUTH_VENCRYPT;
+ if (x509) {
+ VNC_DEBUG("Initializing VNC server with x509 SASL auth\n");
+ vs->subauth = VNC_AUTH_VENCRYPT_X509SASL;
+ } else {
+ VNC_DEBUG("Initializing VNC server with TLS SASL auth\n");
+ vs->subauth = VNC_AUTH_VENCRYPT_TLSSASL;
+ }
+ } else {
+#endif /* CONFIG_VNC_TLS */
+ VNC_DEBUG("Initializing VNC server with SASL auth\n");
+ vs->auth = VNC_AUTH_SASL;
+#ifdef CONFIG_VNC_TLS
+ vs->subauth = VNC_AUTH_INVALID;
+ }
+#endif /* CONFIG_VNC_TLS */
+#endif /* CONFIG_VNC_SASL */
} else {
#ifdef CONFIG_VNC_TLS
if (tls) {
@@ -2529,6 +2773,17 @@ int vnc_display_open(DisplayState *ds, const char
*display, int find_unused)
}
#endif
}
+
+#ifdef CONFIG_VNC_SASL
+ if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) {
+ fprintf(stderr, "Failed to initialize SASL auth %s",
+ sasl_errstring(saslErr, NULL, NULL));
+ free(vs->display);
+ vs->display = NULL;
+ return -1;
+ }
+#endif
+
#ifndef NO_UNIX_SOCKETS
if (strstart(display, "unix:", &p)) {
addr = (struct sockaddr *)&uaddr;
--
2.7.4
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |