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