[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 |