[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