This class can parse the configuration file format described at http://libvirt.org/auth.html to get SPICE authentication information. This uses 'spice' as the service name for the auth-$SERVICE-$HOSTNAME groups. --- Makefile.am | 2 +- configure.ac | 1 + gtk/Makefile.am | 2 + gtk/map-file | 5 + gtk/spice-auth-file.c | 192 +++++++++++++++++++++ gtk/spice-auth-file.h | 62 +++++++ gtk/spice-glib-sym-file | 5 + po/POTFILES.in | 1 + tests/Makefile.am | 26 +++ tests/test-spice-auth-file-data/libvirt/auth.conf | 6 + .../test-spice-auth-file.conf | 25 +++ tests/test-spice-auth-file.c | 144 ++++++++++++++++ 12 files changed, 470 insertions(+), 1 deletion(-) create mode 100644 gtk/spice-auth-file.c create mode 100644 gtk/spice-auth-file.h create mode 100644 tests/Makefile.am create mode 100644 tests/test-spice-auth-file-data/libvirt/auth.conf create mode 100644 tests/test-spice-auth-file-data/test-spice-auth-file.conf create mode 100644 tests/test-spice-auth-file.c diff --git a/Makefile.am b/Makefile.am index ffa1649..ab10f5f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,7 @@ ACLOCAL_AMFLAGS = -I m4 NULL = -SUBDIRS = spice-common gtk po doc data +SUBDIRS = spice-common gtk po doc data tests if HAVE_INTROSPECTION if WITH_VALA diff --git a/configure.ac b/configure.ac index 8ab5b6b..4637bd5 100644 --- a/configure.ac +++ b/configure.ac @@ -669,6 +669,7 @@ gtk/Makefile gtk/controller/Makefile doc/Makefile doc/reference/Makefile +tests/Makefile vapi/Makefile ]) diff --git a/gtk/Makefile.am b/gtk/Makefile.am index d31a396..34ea0c2 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -212,6 +212,7 @@ libspice_client_glib_2_0_la_SOURCES = \ glib-compat.h \ spice-audio.c \ spice-audio-priv.h \ + spice-auth-file.c \ spice-common.h \ spice-util.c \ spice-util-priv.h \ @@ -277,6 +278,7 @@ nodist_libspice_client_glib_2_0_la_SOURCES = \ libspice_client_glibincludedir = $(includedir)/spice-client-glib-2.0 libspice_client_glibinclude_HEADERS = \ spice-audio.h \ + spice-auth-file.h \ spice-client.h \ spice-types.h \ spice-session.h \ diff --git a/gtk/map-file b/gtk/map-file index 03648a8..3d470f4 100644 --- a/gtk/map-file +++ b/gtk/map-file @@ -3,6 +3,11 @@ global: spice_audio_get; spice_audio_get_type; spice_audio_new; +spice_auth_file_get_credential; +spice_auth_file_get_type; +spice_auth_file_lookup_credential; +spice_auth_file_new; +spice_auth_file_new_from_file; spice_channel_connect; spice_channel_destroy; spice_channel_disconnect; diff --git a/gtk/spice-auth-file.c b/gtk/spice-auth-file.c new file mode 100644 index 0000000..bee3467 --- /dev/null +++ b/gtk/spice-auth-file.c @@ -0,0 +1,192 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2013 Red Hat, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ +#include <config.h> +#include <glib/gi18n-lib.h> + +#include "spice-auth-file.h" + +struct _SpiceAuthFilePrivate { + GKeyFile* keyfile; +}; + +G_DEFINE_TYPE(SpiceAuthFile, spice_auth_file, G_TYPE_OBJECT); + +#define SPICE_AUTH_FILE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), SPICE_TYPE_AUTH_FILE, SpiceAuthFilePrivate)) + +/* Lookup auth.conf in this order: + * - The file path specified by the $LIBVIRT_AUTH_FILE environment variable. + * - The file path specified by the "authfile=/some/file" URI query + * parameter + * - The file $XDG_CONFIG_HOME/libvirt/auth.conf + * - The file /etc/libvirt/auth.conf + */ +static SpiceAuthFile* +spice_auth_file_new_with_user_file(const char *user_file, GError** error) +{ + SpiceAuthFile *auth_file; + char *location; + + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + location = (char *)g_getenv("LIBVIRT_AUTH_FILE"); + if (location != NULL) { + auth_file = spice_auth_file_new_from_file(location, NULL); + if (auth_file != NULL) { + return auth_file; + } + } + + if (user_file != NULL) { + auth_file = spice_auth_file_new_from_file(user_file, NULL); + if (auth_file != NULL) { + return auth_file; + } + } + + location = g_build_filename(g_get_user_config_dir(), + "libvirt", "auth.conf", + NULL); + auth_file = spice_auth_file_new_from_file(location, NULL); + g_free(location); + if (auth_file != NULL) { + return auth_file; + } + + auth_file = spice_auth_file_new_from_file("/etc/libvirt/auth.conf", NULL); + if (auth_file != NULL) { + return auth_file; + } + + g_set_error_literal(error, G_FILE_ERROR, G_FILE_ERROR_NOENT, + _("Could not locate auth.conf file")); + + return NULL; +} + +SpiceAuthFile* +spice_auth_file_new(GError** error) +{ + return spice_auth_file_new_with_user_file(NULL, error); +} + + +SpiceAuthFile* +spice_auth_file_new_from_file(const gchar* location, GError** error) +{ + GError* inner_error = NULL; + + g_return_val_if_fail (location != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + SpiceAuthFile* self = SPICE_AUTH_FILE(g_object_new(SPICE_TYPE_AUTH_FILE, NULL)); + GKeyFile* keyfile = self->priv->keyfile; + + g_key_file_load_from_file(keyfile, location, G_KEY_FILE_NONE, &inner_error); + if (inner_error != NULL) { + g_propagate_error(error, inner_error); + g_object_unref(self); + return NULL; + } + + return self; +} + + +char *spice_auth_file_get_credential(SpiceAuthFile *self, + const char *hostname, + const char *credential_name, + GError **error) +{ + char *auth_group; + char *creds; + char *creds_group; + char *credential_value; + + g_return_val_if_fail(SPICE_IS_AUTH_FILE(self), NULL); + g_return_val_if_fail(hostname != NULL, NULL); + g_return_val_if_fail(credential_name != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + auth_group = g_strdup_printf("auth-spice-%s", hostname); + creds = g_key_file_get_string(self->priv->keyfile, + auth_group, "credentials", + error); + if (creds == NULL) + return NULL; + g_free(auth_group); + + creds_group = g_strdup_printf("credentials-%s", creds); + g_free(creds); + + credential_value = g_key_file_get_string(self->priv->keyfile, + creds_group, credential_name, + error); + g_free(creds_group); + + return credential_value; +} + + +char *spice_auth_file_lookup_credential(const char *user_auth_file, + const char *hostname, + const char *credential_name, + GError **error) +{ + SpiceAuthFile *auth_file; + char *credential_value; + + auth_file = spice_auth_file_new_with_user_file(user_auth_file, error); + if (auth_file == NULL) { + return NULL; + } + + credential_value = spice_auth_file_get_credential(auth_file, hostname, + credential_name, error); + g_object_unref(auth_file); + + return credential_value; +} + + +static void +spice_auth_file_finalize(GObject* object) +{ + SpiceAuthFile *self = SPICE_AUTH_FILE(object); + + g_clear_pointer(&self->priv->keyfile, g_key_file_free); + + G_OBJECT_CLASS(spice_auth_file_parent_class)->finalize(object); +} + + +static void +spice_auth_file_init(SpiceAuthFile* self) +{ + self->priv = SPICE_AUTH_FILE_GET_PRIVATE(self); + + self->priv->keyfile = g_key_file_new(); +} + +static void +spice_auth_file_class_init(SpiceAuthFileClass* klass) +{ + spice_auth_file_parent_class = g_type_class_peek_parent(klass); + g_type_class_add_private(klass, sizeof(SpiceAuthFilePrivate)); + + G_OBJECT_CLASS(klass)->finalize = spice_auth_file_finalize; +} diff --git a/gtk/spice-auth-file.h b/gtk/spice-auth-file.h new file mode 100644 index 0000000..f0aa237 --- /dev/null +++ b/gtk/spice-auth-file.h @@ -0,0 +1,62 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2013 Red Hat, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ +#ifndef __SPICE_AUTH_FILE_H__ +#define __SPICE_AUTH_FILE_H__ + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define SPICE_TYPE_AUTH_FILE (spice_auth_file_get_type ()) +#define SPICE_AUTH_FILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SPICE_TYPE_AUTH_FILE, SpiceAuthFile)) +#define SPICE_AUTH_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SPICE_TYPE_AUTH_FILE, SpiceAuthFileClass)) +#define SPICE_IS_AUTH_FILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SPICE_TYPE_AUTH_FILE)) +#define SPICE_IS_AUTH_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SPICE_TYPE_AUTH_FILE)) +#define SPICE_AUTH_FILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SPICE_TYPE_AUTH_FILE, SpiceAuthFileClass)) + +typedef struct _SpiceAuthFile SpiceAuthFile; +typedef struct _SpiceAuthFileClass SpiceAuthFileClass; +typedef struct _SpiceAuthFilePrivate SpiceAuthFilePrivate; + +struct _SpiceAuthFile +{ + GObject parent; + SpiceAuthFilePrivate *priv; +}; + +struct _SpiceAuthFileClass +{ + GObjectClass parent_class; +}; + +GType spice_auth_file_get_type(void); + +SpiceAuthFile* spice_auth_file_new(GError** error); +SpiceAuthFile* spice_auth_file_new_from_file(const gchar* path, GError** error); +char *spice_auth_file_get_credential(SpiceAuthFile *self, const char *hostname, + const char *credential_name, + GError **error); +char *spice_auth_file_lookup_credential(const char *user_auth_file, + const char *hostname, + const char *credential_name, + GError **error); + +G_END_DECLS + +#endif /* __SPICE_AUTH_FILE_H__ */ diff --git a/gtk/spice-glib-sym-file b/gtk/spice-glib-sym-file index fc18388..93227f6 100644 --- a/gtk/spice-glib-sym-file +++ b/gtk/spice-glib-sym-file @@ -1,6 +1,11 @@ spice_audio_get spice_audio_get_type spice_audio_new +spice_auth_file_get_credential +spice_auth_file_get_type +spice_auth_file_lookup_credential +spice_auth_file_new +spice_auth_file_new_from_file spice_channel_connect spice_channel_destroy spice_channel_disconnect diff --git a/po/POTFILES.in b/po/POTFILES.in index 8809121..7f746d4 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -2,6 +2,7 @@ data/spice-mime.xml.in data/spicy.desktop.in.in gtk/channel-usbredir.c gtk/desktop-integration.c +gtk/spice-auth-file.c gtk/spice-cmdline.c gtk/spice-option.c gtk/spicy-screenshot.c diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..f0c77a5 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,26 @@ +noinst_PROGRAMS = test-auth-file + +NULL = + +AM_CPPFLAGS = \ + -I$(top_srcdir)/gtk \ + $(GIO_CFLAGS) \ + $(COMMON_CFLAGS) \ + $(NULL) + +AM_LDFLAGS = \ + $(GIO_LIBS) \ + $(NULL) + +test_auth_file_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -DTEST_SPICE_AUTH_FILE_PATH="\"$(abs_srcdir)\"" \ + $(NULL) +test_auth_file_SOURCES = test-spice-auth-file.c +test_auth_file_LDADD = $(top_builddir)/gtk/libspice-client-glib-2.0.la + +EXTRA_DIST = \ + test-spice-auth-file.conf \ + $(NULL) + +-include $(top_srcdir)/git.mk diff --git a/tests/test-spice-auth-file-data/libvirt/auth.conf b/tests/test-spice-auth-file-data/libvirt/auth.conf new file mode 100644 index 0000000..f5a53cf --- /dev/null +++ b/tests/test-spice-auth-file-data/libvirt/auth.conf @@ -0,0 +1,6 @@ +[credentials-test-spice3] +authname=alice +password=789 + +[auth-spice-test3.example.com] +credentials=test-spice3 diff --git a/tests/test-spice-auth-file-data/test-spice-auth-file.conf b/tests/test-spice-auth-file-data/test-spice-auth-file.conf new file mode 100644 index 0000000..b18212b --- /dev/null +++ b/tests/test-spice-auth-file-data/test-spice-auth-file.conf @@ -0,0 +1,25 @@ +[credentials-test] +authname=fred +password=123456 + +[credentials-test-spice] +authname=bob +password=654321 + +[credentials-test-spice2] +authname=bill + +[auth-libvirt-test1.example.com] +credentials=test + +[auth-spice-test1.example.com] +credentials=test-non-existing + +[auth-spice-test1.example.com:5900] +credentials=test-spice + +[auth-spice-test1.example.com:5901] +credentials=test-non-existing + +[auth-spice-test2.example.com:5900] +credentials=test-spice2 diff --git a/tests/test-spice-auth-file.c b/tests/test-spice-auth-file.c new file mode 100644 index 0000000..50298d0 --- /dev/null +++ b/tests/test-spice-auth-file.c @@ -0,0 +1,144 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2013 Red Hat, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ +#include "spice-auth-file.h" +#include "spice-session.h" + +static void test_auth_file(void) +{ + GError *error = NULL; + SpiceAuthFile *file; + const char *bad_file = "/non/existing/file"; + char *good_file = g_build_filename(TEST_SPICE_AUTH_FILE_PATH, + "test-spice-auth-file-data", + "test-spice-auth-file.conf", + NULL); + char *test_config_dir = g_build_filename(TEST_SPICE_AUTH_FILE_PATH, + "test-spice-auth-file-data", + NULL); + char *username; + char *password; + + /* Set it before calling any g_get_user_XXX functions + * as glib will only read the env variable the first time + * it needs it + */ + g_setenv("XDG_CONFIG_HOME", test_config_dir, TRUE); + + file = spice_auth_file_new_from_file(bad_file, &error); + g_assert(file == NULL); + g_assert_error(error, G_FILE_ERROR, G_FILE_ERROR_NOENT); + g_clear_error(&error); + + file = spice_auth_file_new_from_file(good_file, &error); + g_assert(file != NULL); + g_assert_no_error(error); + g_object_unref(file); + + g_setenv("LIBVIRT_AUTH_FILE", good_file, TRUE); + file = spice_auth_file_new(&error); + g_assert(file != NULL); + g_assert_no_error(error); + g_unsetenv("LIBVIRT_AUTH_FILE"); + + username = spice_auth_file_get_credential(file, "test1.example.com:5900", + "authname", &error); + g_assert_no_error(error); + g_assert_cmpstr(username, ==, "bob"); + g_free(username); + + password = spice_auth_file_get_credential(file, "test1.example.com:5900", + "password", &error); + g_assert_no_error(error); + g_assert_cmpstr(password, ==, "654321"); + g_free(password); + + g_object_unref(file); + + g_setenv("LIBVIRT_AUTH_FILE", good_file, TRUE); + password = spice_auth_file_lookup_credential(NULL, "test1.example.com:5900", + "password", &error); + g_assert_no_error(error); + g_assert_cmpstr(password, ==, "654321"); + g_free(password); + g_unsetenv("LIBVIRT_AUTH_FILE"); + + password = spice_auth_file_lookup_credential(good_file, "test1.example.com:5900", + "password", &error); + g_assert_no_error(error); + g_assert_cmpstr(password, ==, "654321"); + g_free(password); + + g_setenv("LIBVIRT_AUTH_FILE", bad_file, TRUE); + password = spice_auth_file_lookup_credential(NULL, "test1.example.com:5900", + "password", &error); + g_assert_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND); + g_clear_error(&error); + g_unsetenv("LIBVIRT_AUTH_FILE"); + + g_setenv("LIBVIRT_AUTH_FILE", bad_file, TRUE); + password = spice_auth_file_lookup_credential(good_file, "test1.example.com:5900", + "password", &error); + g_assert_no_error(error); + g_assert_cmpstr(password, ==, "654321"); + g_free(password); + g_unsetenv("LIBVIRT_AUTH_FILE"); + + g_setenv("LIBVIRT_AUTH_FILE", good_file, TRUE); + password = spice_auth_file_lookup_credential(NULL, "test3.example.com", + "password", &error); + g_assert_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND); + g_clear_error(&error); + g_unsetenv("LIBVIRT_AUTH_FILE"); + password = spice_auth_file_lookup_credential(good_file, "test3.example.com", + "password", &error); + g_assert_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND); + g_clear_error(&error); + password = spice_auth_file_lookup_credential(NULL, "test3.example.com", + "password", &error); + g_assert_no_error(error); + g_assert_cmpstr(password, ==, "789"); + g_free(password); + g_unsetenv("XDG_CONFIG_HOME"); + + g_free(good_file); +} + +#define TEST_AUTH_FILE "/tmp/spice-auth.conf" +static void test_spice_uri_auth(void) +{ + SpiceSession *session; + char *auth_file; + const char *spice_uri = "spice://example.com:5900?authfile="TEST_AUTH_FILE; + + session = spice_session_new(); + g_object_set(G_OBJECT(session), "uri", spice_uri, NULL); + g_object_get(G_OBJECT(session), "auth-file", &auth_file, NULL); + g_assert_cmpstr(auth_file, ==, TEST_AUTH_FILE); + g_free(auth_file); + g_object_unref(session); +} + +int main(int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/glib/auth-file", test_auth_file); + g_test_add_func ("/glib/auth-file-uri", test_spice_uri_auth); + + return g_test_run(); +} -- 1.8.2.1 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel