mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2025-12-29 08:47:13 +00:00
test-sasl: Base test, connect using SASL
Create a thread that emulates a client and starts SASL authentication Signed-off-by: Frediano Ziglio <fziglio@redhat.com> Acked-by: Christophe Fergeau <cfergeau@redhat.com>
This commit is contained in:
parent
9aa2605611
commit
cbc082ba06
@ -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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user