[Spice-devel] [PATCH 13/13] server: add SASL support
Alon Levy
alevy at redhat.com
Thu Feb 24 13:19:18 PST 2011
On Tue, Feb 22, 2011 at 05:09:07PM +0100, Marc-André Lureau wrote:
> We introduce 2 public functions to integrate with the library user.
>
> spice_server_set_sasl() - turn on SASL
> spice_server_set_sasl_appname() - specify the name of the app (It is
> used for where to find the default configuration file)
>
Do you think this could be made a separate file? reds_sasl.c? Just a thought.
> The patch for QEMU is on its way.
> ---
> server/reds.c | 702 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
> server/reds.h | 39 +++
> server/spice.h | 2 +
> 3 files changed, 737 insertions(+), 6 deletions(-)
>
> diff --git a/server/reds.c b/server/reds.c
> index 0a26e79..1348ff7 100644
> --- a/server/reds.c
> +++ b/server/reds.c
> @@ -16,6 +16,8 @@
> License along with this library; if not, see <http://www.gnu.org/licenses/>.
> */
>
> +#include "config.h"
> +
> #include <stdint.h>
> #include <stdio.h>
> #include <unistd.h>
> @@ -38,6 +40,9 @@
> #include <openssl/rsa.h>
> #include <openssl/ssl.h>
> #include <openssl/err.h>
> +#if HAVE_SASL
> +#include <sasl/sasl.h>
> +#endif
>
> #include "spice.h"
> #include "spice-experimental.h"
> @@ -53,7 +58,6 @@
> #include <spice/stats.h>
> #include "stat.h"
> #include "ring.h"
> -#include "config.h"
> #include "demarshallers.h"
> #include "marshaller.h"
> #include "generated_marshallers.h"
> @@ -83,6 +87,10 @@ static int spice_secure_port = -1;
> static char spice_addr[256];
> static int spice_family = PF_UNSPEC;
> static char *default_renderer = "sw";
> +static int sasl_enabled = 0; // sasl disabled by default
> +#if HAVE_SASL
> +static char *sasl_appname = NULL; // default to "spice" if NULL
> +#endif
>
> static int ticketing_enabled = 1; //Ticketing is enabled by default
> static pthread_mutex_t *lock_cs;
> @@ -1366,7 +1374,10 @@ static void reds_channel_set_common_caps(Channel *channel, int cap, int active)
>
> void reds_channel_init_auth_caps(Channel *channel)
> {
> - reds_channel_set_common_caps(channel, SPICE_COMMON_CAP_AUTH_SPICE, TRUE);
> + if (sasl_enabled)
> + reds_channel_set_common_caps(channel, SPICE_COMMON_CAP_AUTH_SASL, TRUE);
> + else
> + reds_channel_set_common_caps(channel, SPICE_COMMON_CAP_AUTH_SPICE, TRUE);
Just to be sure I understand: we either support SASL authentication, or SPICE, but
never both? unless we talk to a client that doesn't understand the AUTH_SELECTION,
and then we fall back to SPICE?
> reds_channel_set_common_caps(channel, SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION, TRUE);
> }
>
> @@ -1487,7 +1498,7 @@ static void reds_handle_main_link(RedLinkInfo *link)
> link_mess = link->link_mess;
> reds_disconnect();
>
> - if (!link_mess->connection_id) {
> + if (link_mess->connection_id == 0) {
> reds_send_link_result(link, SPICE_LINK_ERR_OK);
> while((connection_id = rand()) == 0);
> reds->agent_state.num_tokens = 0;
> @@ -1671,6 +1682,100 @@ static inline void async_read_clear_handlers(AsyncRead *obj)
> obj->stream->watch = NULL;
> }
>
> +#if HAVE_SASL
> +static int sync_write_u8(RedsStream *s, uint8_t n)
> +{
> + return sync_write(s, &n, sizeof(uint8_t));
> +}
> +
> +static int sync_write_u32(RedsStream *s, uint32_t n)
> +{
> + return sync_write(s, &n, sizeof(uint32_t));
> +}
> +
> +ssize_t reds_stream_sasl_write(RedsStream *s, const void *buf, size_t nbyte)
> +{
> + ssize_t ret;
> +
> + if (!s->sasl.encoded) {
> + int err;
> + err = sasl_encode(s->sasl.conn, (char *)buf, nbyte,
> + (const char **)&s->sasl.encoded,
> + &s->sasl.encodedLength);
> + if (err != SASL_OK) {
> + red_printf("sasl_encode error: %d", err);
> + return -1;
> + }
> +
> + if (s->sasl.encodedLength == 0)
> + return 0;
Missing {}
> + if (!s->sasl.encoded) {
> + red_printf("sasl_encode didn't return a buffer!");
> + return 0;
> + }
> +
> + s->sasl.encodedOffset = 0;
> + }
> +
> + ret = s->write(s, s->sasl.encoded + s->sasl.encodedOffset,
> + s->sasl.encodedLength - s->sasl.encodedOffset);
> +
> + if (ret <= 0)
> + return ret;
Also missing {}
> +
> + s->sasl.encodedOffset += ret;
> + if (s->sasl.encodedOffset == s->sasl.encodedLength) {
> + s->sasl.encoded = NULL;
> + s->sasl.encodedOffset = s->sasl.encodedLength = 0;
> + return nbyte;
> + }
> +
> + /* we didn't flush the encoded buffer */
> + errno = EAGAIN;
> + return -1;
> +}
> +
> +static ssize_t reds_stream_sasl_read(RedsStream *s, void *buf, size_t nbyte)
> +{
> + uint8_t encoded[4096];
> + const char *decoded;
> + unsigned int decodedlen;
> + int err;
> + int n;
> +
> + n = spice_buffer_copy(&s->sasl.inbuffer, buf, nbyte);
> + if (n > 0) {
> + spice_buffer_remove(&s->sasl.inbuffer, n);
> + if (n == nbyte)
> + return n;
> + nbyte -= n;
> + buf += n;
> + }
> +
> + n = s->read(s, encoded, sizeof(encoded));
> + if (n <= 0)
> + return n;
3rd time.
> +
> + err = sasl_decode(s->sasl.conn,
> + (char *)encoded, n,
> + &decoded, &decodedlen);
> + if (err != SASL_OK) {
> + red_printf("sasl_decode error: %d", err);
> + return -1;
> + }
> +
> + if (decodedlen == 0) {
> + errno = EAGAIN;
> + return -1;
> + }
> +
> + n = MIN(nbyte, decodedlen);
> + memcpy(buf, decoded, n);
> + spice_buffer_append(&s->sasl.inbuffer, decoded + n, decodedlen - n);
> + return n;
> +}
> +#endif
> +
> static void async_read_handler(int fd, int event, void *data)
> {
> AsyncRead *obj = (AsyncRead *)data;
> @@ -1723,16 +1828,531 @@ static void reds_get_spice_ticket(RedLinkInfo *link)
> async_read_handler(0, 0, &link->asyc_read);
> }
>
> +#if HAVE_SASL
> +static char *addr_to_string(const char *format,
> + struct sockaddr *sa,
> + socklen_t salen) {
> + char *addr;
> + char host[NI_MAXHOST];
> + char serv[NI_MAXSERV];
> + int err;
> + size_t addrlen;
> +
> + if ((err = getnameinfo(sa, salen,
> + host, sizeof(host),
> + serv, sizeof(serv),
> + NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
> + red_printf("Cannot resolve address %d: %s",
> + 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 = spice_malloc(addrlen + 1);
> + snprintf(addr, addrlen, format, host, serv);
> + addr[addrlen] = '\0';
> +
> + return addr;
> +}
> +
> +static int auth_sasl_check_ssf(RedsSASLContext *sasl, int *runSSF)
> +{
> + const void *val;
> + int err, ssf;
> +
> + *runSSF = 0;
> + if (!sasl->wantSSF)
> + return 1;
> +
> + err = sasl_getprop(sasl->conn, SASL_SSF, &val);
> + if (err != SASL_OK)
> + return 0;
> +
> + ssf = *(const int *)val;
> + red_printf("negotiated an SSF of %d", ssf);
> + if (ssf < 56)
> + return 0; /* 56 is good for Kerberos */
> +
> + *runSSF = 1;
> +
> + /* We have a SSF that's good enough */
> + return 1;
> +}
> +
> +/*
> + * Step Msg
> + *
> + * Input from client:
> + *
> + * u32 clientin-length
> + * u8-array clientin-string
> + *
> + * Output to client:
> + *
> + * u32 serverout-length
> + * u8-array serverout-strin
> + * u8 continue
> + */
> +#define SASL_DATA_MAX_LEN (1024 * 1024)
> +
> +static void reds_handle_auth_sasl_steplen(void *opaque);
> +
> +static void reds_handle_auth_sasl_step(void *opaque)
> +{
> + const char *serverout;
> + unsigned int serveroutlen;
> + int err;
> + char *clientdata = NULL;
> + RedLinkInfo *link = (RedLinkInfo *)opaque;
> + RedsSASLContext *sasl = &link->stream->sasl;
> + uint32_t datalen = sasl->len;
> + AsyncRead *obj = &link->asyc_read;
> +
> + /* NB, distinction of NULL vs "" is *critical* in SASL */
> + if (datalen) {
> + clientdata = sasl->data;
> + clientdata[datalen - 1] = '\0'; /* Wire includes '\0', but make sure */
> + datalen--; /* Don't count NULL byte when passing to _start() */
> + }
> +
> + red_printf("Step using SASL Data %p (%d bytes)",
> + clientdata, datalen);
> + err = sasl_server_step(sasl->conn,
> + clientdata,
> + datalen,
> + &serverout,
> + &serveroutlen);
> + if (err != SASL_OK &&
> + err != SASL_CONTINUE) {
> + red_printf("sasl step failed %d (%s)",
> + err, sasl_errdetail(sasl->conn));
> + goto authabort;
> + }
> +
> + if (serveroutlen > SASL_DATA_MAX_LEN) {
> + red_printf("sasl step reply data too long %d",
> + serveroutlen);
> + goto authabort;
> + }
> +
> + red_printf("SASL return data %d bytes, %p", serveroutlen, serverout);
> +
> + if (serveroutlen) {
> + serveroutlen += 1;
> + sync_write(link->stream, &serveroutlen, sizeof(uint32_t));
> + sync_write(link->stream, serverout, serveroutlen);
> + } else {
> + sync_write(link->stream, &serveroutlen, sizeof(uint32_t));
> + }
> +
> + /* Whether auth is complete */
> + sync_write_u8(link->stream, err == SASL_CONTINUE ? 0 : 1);
> +
> + if (err == SASL_CONTINUE) {
> + red_printf("%s", "Authentication must continue (step)");
> + /* Wait for step length */
> + obj->now = (uint8_t *)&sasl->len;
> + obj->end = obj->now + sizeof(uint32_t);
> + obj->done = reds_handle_auth_sasl_steplen;
> + async_read_handler(0, 0, &link->asyc_read);
> + } else {
> + int ssf;
> +
> + if (auth_sasl_check_ssf(sasl, &ssf) == 0) {
> + red_printf("Authentication rejected for weak SSF");
> + goto authreject;
> + }
> +
> + red_printf("Authentication successful");
> + sync_write_u32(link->stream, SPICE_LINK_ERR_OK); /* Accept auth */
> +
> + /*
> + * Delay writing in SSF encoded until now
> + */
> + sasl->runSSF = ssf;
> + link->stream->writev = NULL; /* make sure writev isn't called directly anymore */
> +
> + reds_handle_link(link);
> + }
> +
> + return;
> +
> +authreject:
> + sync_write_u32(link->stream, 1); /* Reject auth */
> + sync_write_u32(link->stream, sizeof("Authentication failed"));
> + sync_write(link->stream, "Authentication failed", sizeof("Authentication failed"));
> +
> +authabort:
> + reds_link_free(link);
> + return;
> +}
> +
> +static void reds_handle_auth_sasl_steplen(void *opaque)
> +{
> + RedLinkInfo *link = (RedLinkInfo *)opaque;
> + AsyncRead *obj = &link->asyc_read;
> + RedsSASLContext *sasl = &link->stream->sasl;
> +
> + red_printf("Got steplen %d", sasl->len);
> + if (sasl->len > SASL_DATA_MAX_LEN) {
> + red_printf("Too much SASL data %d", sasl->len);
> + reds_link_free(link);
> + return;
> + }
> +
> + if (sasl->len == 0)
> + return reds_handle_auth_sasl_step(opaque);
> + else {
> + sasl->data = spice_realloc(sasl->data, sasl->len);
> + obj->now = (uint8_t *)sasl->data;
> + obj->end = obj->now + sasl->len;
> + obj->done = reds_handle_auth_sasl_step;
> + async_read_handler(0, 0, &link->asyc_read);
> + }
> +}
> +
> +/*
> + * Start Msg
> + *
> + * Input from client:
> + *
> + * u32 clientin-length
> + * u8-array clientin-string
> + *
> + * Output to client:
> + *
> + * u32 serverout-length
> + * u8-array serverout-strin
> + * u8 continue
> + */
> +
> +
> +static void reds_handle_auth_sasl_start(void *opaque)
> +{
> + RedLinkInfo *link = (RedLinkInfo *)opaque;
> + AsyncRead *obj = &link->asyc_read;
> + const char *serverout;
> + unsigned int serveroutlen;
> + int err;
> + char *clientdata = NULL;
> + RedsSASLContext *sasl = &link->stream->sasl;
> + uint32_t datalen = sasl->len;
> +
> + /* NB, distinction of NULL vs "" is *critical* in SASL */
> + if (datalen) {
> + clientdata = sasl->data;
> + clientdata[datalen - 1] = '\0'; /* Should be on wire, but make sure */
> + datalen--; /* Don't count NULL byte when passing to _start() */
> + }
> +
> + red_printf("Start SASL auth with mechanism %s. Data %p (%d bytes)",
> + sasl->mechlist, clientdata, datalen);
> + err = sasl_server_start(sasl->conn,
> + sasl->mechlist,
> + clientdata,
> + datalen,
> + &serverout,
> + &serveroutlen);
> + if (err != SASL_OK &&
> + err != SASL_CONTINUE) {
> + red_printf("sasl start failed %d (%s)",
> + err, sasl_errdetail(sasl->conn));
> + goto authabort;
> + }
> +
> + if (serveroutlen > SASL_DATA_MAX_LEN) {
> + red_printf("sasl start reply data too long %d",
> + serveroutlen);
> + goto authabort;
> + }
> +
> + red_printf("SASL return data %d bytes, %p", serveroutlen, serverout);
> +
> + if (serveroutlen) {
> + serveroutlen += 1;
> + sync_write(link->stream, &serveroutlen, sizeof(uint32_t));
> + sync_write(link->stream, serverout, serveroutlen);
> + } else {
> + sync_write(link->stream, &serveroutlen, sizeof(uint32_t));
> + }
> +
> + /* Whether auth is complete */
> + sync_write_u8(link->stream, err == SASL_CONTINUE ? 0 : 1);
> +
> + if (err == SASL_CONTINUE) {
> + red_printf("%s", "Authentication must continue (start)");
> + /* Wait for step length */
> + obj->now = (uint8_t *)&sasl->len;
> + obj->end = obj->now + sizeof(uint32_t);
> + obj->done = reds_handle_auth_sasl_steplen;
> + async_read_handler(0, 0, &link->asyc_read);
> + } else {
> + int ssf;
> +
> + if (auth_sasl_check_ssf(sasl, &ssf) == 0) {
> + red_printf("Authentication rejected for weak SSF");
> + goto authreject;
> + }
> +
> + red_printf("Authentication successful");
> + sync_write_u32(link->stream, SPICE_LINK_ERR_OK); /* Accept auth */
> +
> + /*
> + * Delay writing in SSF encoded until now
> + */
> + sasl->runSSF = ssf;
> + link->stream->writev = NULL; /* make sure writev isn't called directly anymore */
> +
> + reds_handle_link(link);
> + }
> +
> + return;
> +
> +authreject:
> + sync_write_u32(link->stream, 1); /* Reject auth */
> + sync_write_u32(link->stream, sizeof("Authentication failed"));
> + sync_write(link->stream, "Authentication failed", sizeof("Authentication failed"));
> +
> +authabort:
> + reds_link_free(link);
> + return;
> +}
> +
> +static void reds_handle_auth_startlen(void *opaque)
> +{
> + RedLinkInfo *link = (RedLinkInfo *)opaque;
> + AsyncRead *obj = &link->asyc_read;
> + RedsSASLContext *sasl = &link->stream->sasl;
> +
> + red_printf("Got client start len %d", sasl->len);
> + if (sasl->len > SASL_DATA_MAX_LEN) {
> + red_printf("Too much SASL data %d", sasl->len);
> + reds_send_link_error(link, SPICE_LINK_ERR_INVALID_DATA);
> + reds_link_free(link);
> + return;
> + }
> +
> + if (sasl->len == 0) {
> + reds_handle_auth_sasl_start(opaque);
> + return;
> + }
> +
> + sasl->data = spice_realloc(sasl->data, sasl->len);
> + obj->now = (uint8_t *)sasl->data;
> + obj->end = obj->now + sasl->len;
> + obj->done = reds_handle_auth_sasl_start;
> + async_read_handler(0, 0, &link->asyc_read);
> +}
> +
> +static void reds_handle_auth_mechname(void *opaque)
> +{
> + RedLinkInfo *link = (RedLinkInfo *)opaque;
> + AsyncRead *obj = &link->asyc_read;
> + RedsSASLContext *sasl = &link->stream->sasl;
> +
> + sasl->mechname[sasl->len] = '\0';
> + red_printf("Got client mechname '%s' check against '%s'",
> + sasl->mechname, sasl->mechlist);
> +
> + if (strncmp(sasl->mechlist, sasl->mechname, sasl->len) == 0) {
> + if (sasl->mechlist[sasl->len] != '\0' &&
> + sasl->mechlist[sasl->len] != ',') {
> + red_printf("One %d", sasl->mechlist[sasl->len]);
> + reds_link_free(link);
> + return;
> + }
> + } else {
> + char *offset = strstr(sasl->mechlist, sasl->mechname);
> + red_printf("Two %p", offset);
> + if (!offset) {
> + reds_send_link_error(link, SPICE_LINK_ERR_INVALID_DATA);
> + return;
> + }
> + red_printf("Two '%s'", offset);
> + if (offset[-1] != ',' ||
> + (offset[sasl->len] != '\0'&&
> + offset[sasl->len] != ',')) {
> + reds_send_link_error(link, SPICE_LINK_ERR_INVALID_DATA);
> + return;
> + }
> + }
> +
> + free(sasl->mechlist);
> + sasl->mechlist = strdup(sasl->mechname);
> +
> + red_printf("Validated mechname '%s'", sasl->mechname);
> +
> + obj->now = (uint8_t *)&sasl->len;
> + obj->end = obj->now + sizeof(uint32_t);
> + obj->done = reds_handle_auth_startlen;
> + async_read_handler(0, 0, &link->asyc_read);
> +
> + return;
> +}
> +
> +static void reds_handle_auth_mechlen(void *opaque)
> +{
> + RedLinkInfo *link = (RedLinkInfo *)opaque;
> + AsyncRead *obj = &link->asyc_read;
> + RedsSASLContext *sasl = &link->stream->sasl;
> +
> + if (sasl->len < 1 || sasl->len > 100) {
> + red_printf("Got client mechname len %d", sasl->len);
"Got bad client mechname len %d" FTFY.
> + reds_link_free(link);
> + return;
> + }
> +
> + sasl->mechname = spice_malloc(sasl->len + 1);
> +
> + red_printf("Wait for client mechname");
> + obj->now = (uint8_t *)sasl->mechname;
> + obj->end = obj->now + sasl->len;
> + obj->done = reds_handle_auth_mechname;
> + async_read_handler(0, 0, &link->asyc_read);
> +}
> +
> +static void reds_start_auth_sasl(RedLinkInfo *link)
> +{
> + const char *mechlist = NULL;
> + sasl_security_properties_t secprops;
> + int err;
> + char *localAddr, *remoteAddr;
> + int mechlistlen;
> + AsyncRead *obj = &link->asyc_read;
> + RedsSASLContext *sasl = &link->stream->sasl;
> +
> + /* Get local & remote client addresses in form IPADDR;PORT */
> + if (!(localAddr = addr_to_string("%s;%s", &link->stream->info.laddr, link->stream->info.llen)))
> + goto error;
> +
> + if (!(remoteAddr = addr_to_string("%s;%s", &link->stream->info.paddr, link->stream->info.plen))) {
> + free(localAddr);
> + goto error;
> + }
> +
> + err = sasl_server_new("spice",
> + NULL, /* FQDN - just delegates to gethostname */
> + NULL, /* User realm */
> + localAddr,
> + remoteAddr,
> + NULL, /* Callbacks, not needed */
> + SASL_SUCCESS_DATA,
> + &sasl->conn);
> + free(localAddr);
> + free(remoteAddr);
> + localAddr = remoteAddr = NULL;
> +
> + if (err != SASL_OK) {
> + red_printf("sasl context setup failed %d (%s)",
> + err, sasl_errstring(err, NULL, NULL));
> + sasl->conn = NULL;
> + goto error;
> + }
> +
> + /* Inform SASL that we've got an external SSF layer from TLS */
> + if (link->stream->ssl) {
> + sasl_ssf_t ssf;
> +
> + ssf = SSL_get_cipher_bits(link->stream->ssl, NULL);
> + err = sasl_setprop(sasl->conn, SASL_SSF_EXTERNAL, &ssf);
> + if (err != SASL_OK) {
> + red_printf("cannot set SASL external SSF %d (%s)",
> + err, sasl_errstring(err, NULL, NULL));
> + sasl_dispose(&sasl->conn);
> + sasl->conn = NULL;
> + goto error;
> + }
> + } else
> + sasl->wantSSF = 1;
Add {}.
> +
> + memset(&secprops, 0, sizeof secprops);
> + /* Inform SASL that we've got an external SSF layer from TLS */
> + if (link->stream->ssl) {
> + /* If we've got TLS (or UNIX domain sock), we don't care about SSF */
> + secprops.min_ssf = 0;
> + secprops.max_ssf = 0;
> + secprops.maxbufsize = 8192;
> + secprops.security_flags = 0;
> + } else {
> + /* Plain TCP, better get an SSF layer */
> + secprops.min_ssf = 56; /* Good enough to require kerberos */
> + secprops.max_ssf = 100000; /* Arbitrary big number */
> + secprops.maxbufsize = 8192;
> + /* Forbid any anonymous or trivially crackable auth */
> + secprops.security_flags =
> + SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
> + }
> +
> + err = sasl_setprop(sasl->conn, SASL_SEC_PROPS, &secprops);
> + if (err != SASL_OK) {
> + red_printf("cannot set SASL security props %d (%s)",
> + err, sasl_errstring(err, NULL, NULL));
> + sasl_dispose(&sasl->conn);
> + sasl->conn = NULL;
> + goto error;
> + }
> +
> + err = sasl_listmech(sasl->conn,
> + NULL, /* Don't need to set user */
> + "", /* Prefix */
> + ",", /* Separator */
> + "", /* Suffix */
> + &mechlist,
> + NULL,
> + NULL);
> + if (err != SASL_OK) {
> + red_printf("cannot list SASL mechanisms %d (%s)",
> + err, sasl_errdetail(sasl->conn));
> + sasl_dispose(&sasl->conn);
> + sasl->conn = NULL;
> + goto error;
> + }
> + red_printf("Available mechanisms for client: '%s'", mechlist);
> +
> + sasl->mechlist = strdup(mechlist);
> +
> + mechlistlen = strlen(mechlist);
> + if (!sync_write(link->stream, &mechlistlen, sizeof(uint32_t))
> + || !sync_write(link->stream, sasl->mechlist, mechlistlen)) {
> + red_printf("SASL mechanisms write error");
> + goto error;
> + }
> +
> + red_printf("Wait for client mechname length");
> + obj->now = (uint8_t *)&sasl->len;
> + obj->end = obj->now + sizeof(uint32_t);
> + obj->done = reds_handle_auth_mechlen;
> + async_read_handler(0, 0, &link->asyc_read);
> +
> + return;
> +
> +error:
> + reds_link_free(link);
> + return;
> +}
> +#endif
> +
> static void reds_handle_auth_mechanism(void *opaque)
> {
> RedLinkInfo *link = (RedLinkInfo *)opaque;
>
> red_printf("Auth method: %d", link->auth_mechanism.auth_mechanism);
>
> - if (link->auth_mechanism.auth_mechanism == SPICE_COMMON_CAP_AUTH_SPICE) {
> + if (link->auth_mechanism.auth_mechanism == SPICE_COMMON_CAP_AUTH_SPICE
> + && !sasl_enabled
> + ) {
> reds_get_spice_ticket(link);
> +#if HAVE_SASL
> + } else if (link->auth_mechanism.auth_mechanism == SPICE_COMMON_CAP_AUTH_SASL) {
> + red_printf("Starting SASL");
> + reds_start_auth_sasl(link);
> +#endif
> } else {
> red_printf("Unknown auth method, disconnecting");
> + if (sasl_enabled)
> + red_printf("Your client doesn't handle SASL?");
> reds_send_link_error(link, SPICE_LINK_ERR_INVALID_DATA);
> reds_link_free(link);
> }
> @@ -1784,6 +2404,11 @@ static void reds_handle_read_link_done(void *opaque)
> }
>
> if (!auth_selection) {
> + if (sasl_enabled) {
> + red_printf("SASL enabled, but peer supports only spice authentication");
> + reds_send_link_error(link, SPICE_LINK_ERR_VERSION_MISMATCH);
> + return;
> + }
> red_printf("Peer doesn't support AUTH selection");
> reds_get_spice_ticket(link);
> } else {
> @@ -2846,6 +3471,16 @@ static int do_spice_init(SpiceCoreInterface *core_interface)
> if (reds->secure_listen_socket != -1) {
> reds_init_ssl();
> }
> +#if HAVE_SASL
> + int saslerr;
> + if ((saslerr = sasl_server_init(NULL, sasl_appname ?
> + sasl_appname : "spice")) != SASL_OK) {
> + red_error("Failed to initialize SASL auth %s",
> + sasl_errstring(saslerr, NULL, NULL));
> + goto err;
> + }
> +#endif
> +
> reds->main_channel = NULL;
> inputs_init();
>
> @@ -2939,6 +3574,29 @@ __visible__ int spice_server_set_noauth(SpiceServer *s)
> return 0;
> }
>
> +__visible__ int spice_server_set_sasl(SpiceServer *s, int enabled)
> +{
> + ASSERT(reds == s);
> +#if HAVE_SASL
> + sasl_enabled = enabled;
> + return 0;
> +#else
> + return -1;
> +#endif
> +}
> +
> +__visible__ int spice_server_set_sasl_appname(SpiceServer *s, const char *appname)
> +{
> + ASSERT(reds == s);
> +#if HAVE_SASL
> + free(sasl_appname);
> + sasl_appname = strdup(appname);
> + return 0;
> +#else
> + return -1;
> +#endif
> +}
> +
> __visible__ int spice_server_set_ticket(SpiceServer *s,
> const char *passwd, int lifetime,
> int fail_if_connected,
> @@ -3214,12 +3872,30 @@ __visible__ int spice_server_migrate_switch(SpiceServer *s)
>
> ssize_t reds_stream_read(RedsStream *s, void *buf, size_t nbyte)
> {
> - return s->read(s, buf, nbyte);
> + ssize_t ret;
> +
> +#if HAVE_SASL
> + if (s->sasl.conn && s->sasl.runSSF)
{}
> + ret = reds_stream_sasl_read(s, buf, nbyte);
> + else
{}
> +#endif
> + ret = s->read(s, buf, nbyte);
> +
> + return ret;
> }
>
> ssize_t reds_stream_write(RedsStream *s, const void *buf, size_t nbyte)
> {
> - return s->write(s, buf, nbyte);
> + ssize_t ret;
> +
> +#if HAVE_SASL
> + if (s->sasl.conn && s->sasl.runSSF)
> + ret = reds_stream_sasl_write(s, buf, nbyte);
{}
> + else
> +#endif
> + ret = s->write(s, buf, nbyte);
{}
> +
Wouldn't this be nicer using just a callback? (like olden times) Well, I don't
care to write that patch, so I don't mind if this stays.
> + return ret;
> }
>
> ssize_t reds_stream_writev(RedsStream *s, const struct iovec *iov, int iovcnt)
> @@ -3248,6 +3924,20 @@ void reds_stream_free(RedsStream *s)
>
> spice_channel_event(&s->info, SPICE_CHANNEL_EVENT_DISCONNECTED);
>
> +#if HAVE_SASL
> + if (s->sasl.conn) {
> + s->sasl.runSSF = s->sasl.wantSSF = 0;
> + s->sasl.len = 0;
> + s->sasl.encodedLength = s->sasl.encodedOffset = 0;
> + s->sasl.encoded = NULL;
> + free(s->sasl.mechlist);
> + free(s->sasl.mechname);
> + s->sasl.mechlist = NULL;
> + sasl_dispose(&s->sasl.conn);
> + s->sasl.conn = NULL;
> + }
> +#endif
> +
> if (s->ssl)
> SSL_free(s->ssl);S
{}
>
> diff --git a/server/reds.h b/server/reds.h
> index f0276b1..ff015b2 100644
> --- a/server/reds.h
> +++ b/server/reds.h
> @@ -18,10 +18,16 @@
> #ifndef _H_REDS
> #define _H_REDS
>
> +#include "config.h"
> +
> #include <stdint.h>
> #include <openssl/ssl.h>
> #include <sys/uio.h>
> #include <spice/vd_agent.h>
> +#if HAVE_SASL
> +#include <sasl/sasl.h>
> +#endif
> +
> #include "common/marshaller.h"
> #include "common/messages.h"
> #include "spice.h"
> @@ -30,6 +36,35 @@
>
> typedef struct RedsStream RedsStream;
>
> +#if HAVE_SASL
> +typedef struct RedsSASLContext {
So you use Context here after removing it from RedsStream??
How about just RedsSASL?
> + sasl_conn_t *conn;
> +
> + /* If we want to negotiate an SSF layer with client */
> + int wantSSF :1;
> + /* If we are now running the SSF layer */
> + int runSSF :1;
> +
> + /*
> + * Buffering encoded data to allow more clear data
> + * to be stuffed onto the output buffer
> + */
> + const uint8_t *encoded;
> + unsigned int encodedLength;
> + unsigned int encodedOffset;
> +
> + SpiceBuffer inbuffer;
> +
> + char *username;
> + char *mechlist;
> + char *mechname;
> +
> + /* temporary data during authentication */
> + unsigned int len;
> + char *data;
> +} RedsSASLContext;
> +#endif
> +
> struct RedsStream {
> int socket;
> SpiceWatch *watch;
> @@ -39,6 +74,10 @@ struct RedsStream {
> int shutdown;
> SSL *ssl;
>
> +#if HAVE_SASL
> + RedsSASLContext sasl;
> +#endif
> +
> SpiceChannelEventInfo info;
>
> /* private */
> diff --git a/server/spice.h b/server/spice.h
> index 6fb22a4..7e85ad7 100644
> --- a/server/spice.h
> +++ b/server/spice.h
> @@ -374,6 +374,8 @@ int spice_server_set_compat_version(SpiceServer *s,
> int spice_server_set_port(SpiceServer *s, int port);
> void spice_server_set_addr(SpiceServer *s, const char *addr, int flags);
> int spice_server_set_noauth(SpiceServer *s);
> +int spice_server_set_sasl(SpiceServer *s, int enabled);
> +int spice_server_set_sasl_appname(SpiceServer *s, const char *appname);
> int spice_server_set_ticket(SpiceServer *s, const char *passwd, int lifetime,
> int fail_if_connected, int disconnect_if_connected);
> int spice_server_set_tls(SpiceServer *s, int port,
> --
> 1.7.4
>
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel
More information about the Spice-devel
mailing list