[Spice-devel] [PATCH spice-server 10/16] test-sasl: Base test, connect using SASL

Frediano Ziglio fziglio at redhat.com
Thu Dec 14 10:07:42 UTC 2017


Create a thread that emulate a client and start SASL authentication

Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
---
 server/tests/test-sasl.c | 236 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 235 insertions(+), 1 deletion(-)

diff --git a/server/tests/test-sasl.c b/server/tests/test-sasl.c
index 4b1d778c0..500164157 100644
--- a/server/tests/test-sasl.c
+++ b/server/tests/test-sasl.c
@@ -22,19 +22,38 @@
 
 #if HAVE_SASL
 #include <unistd.h>
-#include <spice.h>
+#include <errno.h>
 #include <stdbool.h>
+#include <spice.h>
 #include <sasl/sasl.h>
 
+#include <spice/protocol.h>
+#include <common/macros.h>
+
 #include "test-glib-compat.h"
 #include "basic-event-loop.h"
 
+#include <spice/start-packed.h>
+typedef struct SPICE_ATTR_PACKED SpiceInitialMessage {
+        SpiceLinkHeader hdr;
+        SpiceLinkMess mess;
+        uint32_t caps[2];
+} SpiceInitialMessage;
+#include <spice/end-packed.h>
+
 static char *mechlist;
 static bool mechlist_called;
 static bool start_called;
 static bool step_called;
 static bool encode_called;
 
+static SpiceCoreInterface *core;
+static SpiceServer *server;
+
+static int test_num = -1;
+
+static gboolean idle_next_test(void *arg);
+
 static void
 check_sasl_conn(sasl_conn_t *conn)
 {
@@ -197,9 +216,224 @@ sasl_server_step(sasl_conn_t *conn,
     return SASL_OK;
 }
 
+static SpiceInitialMessage initial_message = {
+    {
+        0, // SPICE_MAGIC,
+        GUINT32_TO_LE(SPICE_VERSION_MAJOR), GUINT32_TO_LE(SPICE_VERSION_MINOR),
+        GUINT32_TO_LE(sizeof(SpiceInitialMessage) - sizeof(SpiceLinkHeader))
+    },
+    {
+        0,
+        SPICE_CHANNEL_MAIN,
+        0,
+        GUINT32_TO_LE(1),
+        GUINT32_TO_LE(1),
+        GUINT32_TO_LE(sizeof(SpiceLinkMess))
+    },
+    {
+        GUINT32_TO_LE(SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION|SPICE_COMMON_CAP_AUTH_SASL|
+                      SPICE_COMMON_CAP_MINI_HEADER),
+        0
+    }
+};
+
+static void
+reset_test(void)
+{
+    mechlist_called = false;
+    start_called = false;
+    step_called = false;
+    encode_called = false;
+}
+
+static void
+start_test(void)
+{
+    g_assert_null(server);
+
+    initial_message.hdr.magic = SPICE_MAGIC;
+
+    reset_test();
+
+    core = basic_event_loop_init();
+    g_assert_nonnull(core);
+
+    server = spice_server_new();
+    g_assert_nonnull(server);
+    spice_server_set_sasl(server, true);
+    g_assert_cmpint(spice_server_init(server, core), ==, 0);
+}
+
+static void
+end_test(void)
+{
+    spice_server_destroy(server);
+    server = NULL;
+
+    basic_event_loop_destroy();
+    core = NULL;
+
+    g_free(mechlist);
+    mechlist = NULL;
+}
+
+static size_t
+do_readwrite_all(int fd, const void *buf, const size_t len, bool do_write)
+{
+    size_t written = 0;
+    while (written < len) {
+        int l;
+        if (do_write) {
+            l = write(fd, (const char *) buf + written, len - written);
+        } else {
+            l = read(fd, (char *) buf + written, len - written);
+            if (l == 0) {
+                return written;
+            }
+        }
+        if (l < 0 && errno == EINTR) {
+            continue;
+        }
+        if (l < 0) {
+            return l;
+        }
+        written += l;
+    }
+    return written;
+}
+
+// use macro to maintain line number on error
+#define read_all(fd, buf, len) \
+    g_assert_cmpint(do_readwrite_all(fd, buf, len, false), ==, len)
+
+#define write_all(fd, buf, len) \
+    g_assert_cmpint(do_readwrite_all(fd, buf, len, true), ==, len)
+
+static ssize_t
+read_u32_err(int fd, uint32_t *out)
+{
+    uint32_t val = 0;
+    ssize_t ret = do_readwrite_all(fd, &val, sizeof(val), false);
+    *out = GUINT32_FROM_LE(val);
+    return ret;
+}
+#define read_u32(fd, out) \
+    g_assert_cmpint(read_u32_err(fd, out), ==, sizeof(uint32_t))
+
+static ssize_t
+write_u32_err(int fd, uint32_t val)
+{
+    val = GUINT32_TO_LE(val);
+    return do_readwrite_all(fd, &val, sizeof(val), true);
+}
+
+#define write_u32(fd, val) \
+    g_assert_cmpint(write_u32_err(fd, val), ==, sizeof(uint32_t))
+
+static void
+idle_add(GSourceFunc func, void *arg)
+{
+    GSource *source = g_idle_source_new();
+    g_source_set_callback(source, func, NULL, NULL);
+    g_source_attach(source, basic_event_loop_get_context());
+    g_source_unref(source);
+}
+
+static void *
+client_emulator(void *arg)
+{
+    int sock = GPOINTER_TO_INT(arg);
+
+    // send initial message
+    write_all(sock, &initial_message, sizeof(initial_message));
+
+    // server replies link ack with rsa, etc, similar to above beside
+    // fixed fields
+    struct {
+        SpiceLinkHeader header;
+        SpiceLinkReply ack;
+    } msg;
+    SPICE_VERIFY(sizeof(msg) == sizeof(SpiceLinkHeader) + sizeof(SpiceLinkReply));
+    read_all(sock, &msg, sizeof(msg));
+    uint32_t num_caps = GUINT32_FROM_LE(msg.ack.num_common_caps) + GUINT32_FROM_LE(msg.ack.num_channel_caps);
+    for (; num_caps; --num_caps) {
+        uint32_t cap;
+        read_all(sock, &cap, sizeof(cap));
+    }
+
+    // client have to send a SpiceLinkAuthMechanism (just uin32 with
+    // mech SPICE_COMMON_CAP_AUTH_SASL)
+    write_u32(sock, SPICE_COMMON_CAP_AUTH_SASL);
+
+    // sasl finally start, data starts from server (mech list)
+    //
+    uint32_t mechlen;
+    read_u32(sock, &mechlen);
+    char buf[300];
+    g_assert(mechlen <= sizeof(buf));
+    read_all(sock, buf, mechlen);
+
+    // mech name
+    write_u32(sock, 3);
+    write_all(sock, "ONE", 3);
+
+    // first challenge
+    write_u32(sock, 5);
+    write_all(sock, "START", 5);
+
+    shutdown(sock, SHUT_RDWR);
+    close(sock);
+
+    idle_add(idle_next_test, NULL);
+
+    return NULL;
+}
+
+// called when a new test has to be run
+static gboolean
+idle_next_test(void *arg)
+{
+    // end previous test
+    if (test_num >= 0) {
+        g_assert(encode_called);
+        reset_test();
+        basic_event_loop_quit();
+        return FALSE;
+    }
+
+    // start next test
+    ++test_num;
+    alarm(4);
+
+    int sv[2];
+    g_assert_cmpint(socketpair(AF_LOCAL, SOCK_STREAM, 0, sv), ==, 0);
+
+    g_assert(spice_server_add_client(server, sv[0], 0) == 0);
+
+    pthread_t thread;
+    g_assert_cmpint(pthread_create(&thread, NULL, client_emulator, GINT_TO_POINTER(sv[1])), ==, 0);
+    g_assert_cmpint(pthread_detach(thread), ==, 0);
+
+    return FALSE;
+}
+
+static void
+sasl_mechs(void)
+{
+    start_test();
+
+    idle_add(idle_next_test, NULL);
+    alarm(4);
+    basic_event_loop_mainloop();
+
+    end_test();
+}
+
 int
 main(int argc, char *argv[])
 {
+    sasl_mechs();
+
     return 0;
 }
 #else
-- 
2.14.3



More information about the Spice-devel mailing list