[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