Create a thread that emulates a client and starts SASL authentication Signed-off-by: Frediano Ziglio <fziglio@xxxxxxxxxx> --- 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 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel