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

Frediano Ziglio fziglio at redhat.com
Tue Jan 9 07:44:58 UTC 2018


Create a thread that emulates a client and starts SASL authentication

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

diff --git a/server/tests/test-sasl.c b/server/tests/test-sasl.c
index 85332974..41bcf7e8 100644
--- a/server/tests/test-sasl.c
+++ b/server/tests/test-sasl.c
@@ -21,19 +21,36 @@
 #include <config.h>
 
 #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 gboolean idle_end_test(void *arg);
+
 static void
 check_sasl_conn(sasl_conn_t *conn)
 {
@@ -196,8 +213,226 @@ 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_tests(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 byte_count = 0;
+    while (byte_count < len) {
+        int l;
+        if (do_write) {
+            l = write(fd, (const char *) buf + byte_count, len - byte_count);
+        } else {
+            l = read(fd, (char *) buf + byte_count, len - byte_count);
+            if (l == 0) {
+                return byte_count;
+            }
+        }
+        if (l < 0 && errno == EINTR) {
+            continue;
+        }
+        if (l < 0) {
+            return l;
+        }
+        byte_count += l;
+    }
+    return byte_count;
+}
+
+// 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))
+
+/* This function is similar to g_idle_add but uses our internal Glib
+ * main context. g_idle_add uses the default main context but to make
+ * sure we can use a different main context we don't use the default
+ * one (as Qemu does) */
+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);
+    while (num_caps-- > 0) {
+        uint32_t cap;
+        read_all(sock, &cap, sizeof(cap));
+    }
+
+    // client have to send a SpiceLinkAuthMechanism (just uint32 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_cmpint(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_end_test, NULL);
+
+    return NULL;
+}
+
+static pthread_t
+setup_thread(void)
+{
+    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);
+    return thread;
+}
+
+// called when the next test has to be run
+static gboolean
+idle_end_test(void *arg)
+{
+    basic_event_loop_quit();
+
+    return G_SOURCE_REMOVE;
+}
+
+static void
+sasl_mechs(void)
+{
+    start_test();
+
+    pthread_t thread = setup_thread();
+    alarm(4);
+    basic_event_loop_mainloop();
+    g_assert_cmpint(pthread_join(thread, NULL), ==, 0);
+    alarm(0);
+    g_assert(encode_called);
+    reset_test();
+
+    end_tests();
+}
+
 int
 main(int argc, char *argv[])
 {
+    sasl_mechs();
+
     return 0;
 }
-- 
2.14.3



More information about the Spice-devel mailing list