[vlc-devel] [PATCH 04/11] keystore: add libsecret module
Thomas Guillem
thomas at gllm.fr
Wed Jan 6 16:56:24 CET 2016
---
configure.ac | 5 +
modules/keystore/Makefile.am | 11 ++
modules/keystore/secret.c | 346 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 362 insertions(+)
create mode 100644 modules/keystore/secret.c
diff --git a/configure.ac b/configure.ac
index 1a3756d..362e4bf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4065,6 +4065,11 @@ AS_IF([test "${enable_taglib}" != "no"], [
])
dnl
+dnl libsecret
+dnl
+PKG_ENABLE_MODULES_VLC([SECRET], [], [libsecret-1], [use libsecret for keystore], [auto])
+
+dnl
dnl Developers helper modules (should be hidden from configure help)
dnl
AC_ARG_ENABLE(devtools, [], [], [enable_devtools="no"])
diff --git a/modules/keystore/Makefile.am b/modules/keystore/Makefile.am
index 34cbf00..dc8c526 100644
--- a/modules/keystore/Makefile.am
+++ b/modules/keystore/Makefile.am
@@ -2,3 +2,14 @@ keystoredir = $(pluginsdir)/keystore
libplaintext_keystore_plugin_la_SOURCES = keystore/plaintext.c
keystore_LTLIBRARIES = libplaintext_keystore_plugin.la
+
+libsecret_plugin_la_SOURCES = keystore/secret.c
+libsecret_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) $(SECRET_CFLAGS)
+libsecret_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(keystoredir)'
+libsecret_plugin_la_LIBADD = $(SECRET_LIBS)
+
+keystore_LTLIBRARIES += \
+ $(LTLIBsecret)
+
+EXTRA_LTLIBRARIES += \
+ libsecret_plugin.la
diff --git a/modules/keystore/secret.c b/modules/keystore/secret.c
new file mode 100644
index 0000000..9bb5f5b
--- /dev/null
+++ b/modules/keystore/secret.c
@@ -0,0 +1,346 @@
+/*****************************************************************************
+ * secret.c: libsecret keystore module
+ *****************************************************************************
+ * Copyright © 2015-2016 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program 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 program 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_keystore.h>
+#include <vlc_interrupt.h>
+
+#include <assert.h>
+
+#include <libsecret/secret.h>
+#include <gio/gdbusnamewatching.h>
+
+static int Open(vlc_object_t *);
+static void Close(vlc_object_t *);
+
+vlc_module_begin()
+ set_shortname(N_("libsecret keystore"))
+ set_description(N_("secrets are stored via libsecret"))
+ set_category(CAT_ADVANCED)
+ set_subcategory(SUBCAT_ADVANCED_MISC)
+ set_capability("keystore", 100)
+ set_callbacks(Open, Close)
+ /* Since we can't destroy gdbus_shared_thread_func */
+ cannot_unload_broken_library()
+vlc_module_end ()
+
+static const char *const ppsz_keys[] = {
+ "protocol",
+ "user",
+ "server",
+ "path",
+ "port",
+ "realm",
+ "authtype",
+};
+static_assert(sizeof(ppsz_keys)/sizeof(*ppsz_keys) == KEY_MAX, "key mismatch");
+
+static int
+str2key(const char *psz_key)
+{
+ for (unsigned int i = 0; i < KEY_MAX; ++i)
+ {
+ if (strcmp(ppsz_keys[i], psz_key) == 0)
+ return i;
+ }
+ return -1;
+}
+
+static void cancellable_interrupted(void *p_data)
+{
+ GCancellable *p_canc = p_data;
+ g_cancellable_cancel(p_canc);
+}
+
+static GCancellable *cancellable_register()
+{
+ GCancellable *p_canc = g_cancellable_new();
+ if (!p_canc)
+ return NULL;
+ vlc_interrupt_register(cancellable_interrupted, p_canc);
+ return p_canc;
+}
+
+static void cancellable_unregister(GCancellable *p_canc)
+{
+ if (p_canc != NULL)
+ {
+ vlc_interrupt_unregister();
+ g_object_unref(p_canc);
+ }
+}
+
+static GHashTable *
+values_to_ghashtable(const char *const ppsz_values[KEY_MAX])
+{
+ GHashTable *p_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, NULL);
+ if (!p_hash)
+ return NULL;
+ for (unsigned int i = 0; i < KEY_MAX; ++i)
+ {
+ if (ppsz_values[i])
+ g_hash_table_insert(p_hash, (gpointer) ppsz_keys[i],
+ (gpointer) ppsz_values[i]);
+ }
+ return p_hash;
+}
+
+static void
+ghash_to_value(gpointer key, gpointer value, gpointer user_data)
+{
+ const char **ppsz_values = user_data;
+
+ const char *psz_key = key;
+ int i_key = str2key(psz_key);
+ if (i_key == -1 || i_key >= KEY_MAX)
+ return;
+
+ ppsz_values[i_key] = strdup((const char *)value);
+}
+
+static int
+ghashtable_to_values(GHashTable *g_hash, const char *ppsz_values[KEY_MAX])
+{
+ g_hash_table_foreach(g_hash, ghash_to_value, ppsz_values);
+ return VLC_SUCCESS;
+}
+
+static void
+ghashtable_insert_vlc_id(GHashTable *g_hash)
+{
+ g_hash_table_insert(g_hash, (gpointer) ".created_by",
+ (gpointer) VLC_KEYSTORE_NAME);
+}
+
+static int
+Store(vlc_keystore *p_keystore, const char *const ppsz_values[KEY_MAX],
+ const uint8_t *p_secret, size_t i_secret_len, const char *psz_label)
+{
+ SecretService *p_ss = (SecretService *) p_keystore->p_sys;
+ GHashTable *p_hash = values_to_ghashtable(ppsz_values);
+ if (!p_hash)
+ return VLC_EGENERIC;
+ ghashtable_insert_vlc_id(p_hash);
+
+ SecretValue *p_sv = secret_value_new((const gchar *)p_secret, i_secret_len,
+ "text/plain");
+ if (!p_sv)
+ {
+ g_hash_table_unref(p_hash);
+ return VLC_EGENERIC;
+ }
+
+ GCancellable *p_canc = cancellable_register();
+ gboolean b_ret = secret_service_store_sync(p_ss, NULL, p_hash,
+ SECRET_COLLECTION_DEFAULT,
+ psz_label, p_sv, p_canc, NULL);
+ cancellable_unregister(p_canc);
+
+ secret_value_unref(p_sv);
+ g_hash_table_unref(p_hash);
+ return b_ret ? VLC_SUCCESS : VLC_EGENERIC;
+}
+
+static GList*
+items_search(SecretService *p_ss, const char *const ppsz_values[KEY_MAX],
+ bool b_safe)
+{
+ GHashTable *p_hash = values_to_ghashtable(ppsz_values);
+ if (!p_hash)
+ return 0;
+
+ /* If true, do not allow to remove non VLC entries */
+ if (b_safe)
+ ghashtable_insert_vlc_id(p_hash);
+
+ GCancellable *p_canc = cancellable_register();
+ GList *p_list = secret_service_search_sync(p_ss, NULL, p_hash,
+ SECRET_SEARCH_ALL
+ | SECRET_SEARCH_UNLOCK
+ | SECRET_SEARCH_LOAD_SECRETS,
+ p_canc, NULL);
+ cancellable_unregister(p_canc);
+ g_hash_table_unref(p_hash);
+ return p_list;
+}
+
+static unsigned int
+Find(vlc_keystore *p_keystore, const char *const ppsz_values[KEY_MAX],
+ vlc_keystore_entry **pp_entries)
+{
+ SecretService *p_ss = (SecretService *) p_keystore->p_sys;
+
+ GList *p_list = items_search(p_ss, ppsz_values, false);
+ if (!p_list)
+ return 0;
+
+ unsigned int i_found_count = g_list_length(p_list);
+ unsigned int i_entry_count = 0;
+ vlc_keystore_entry *p_entries = calloc(i_found_count,
+ sizeof(vlc_keystore_entry));
+ if (!p_entries)
+ goto error;
+
+ for (GList *l = p_list; l != NULL; l = l->next)
+ {
+ SecretItem *p_item = (SecretItem *) l->data;
+ GHashTable *p_attrs = secret_item_get_attributes(p_item);
+
+ vlc_keystore_entry *p_entry = &p_entries[i_entry_count++];
+ /* fill ppsz_values */
+ if (ghashtable_to_values(p_attrs, (const char **) p_entry->ppsz_values))
+ {
+ g_hash_table_unref(p_attrs);
+ goto error;
+ }
+ g_hash_table_unref(p_attrs);
+
+ /* fill secret */
+ SecretValue *p_secret_value = secret_item_get_secret(p_item);
+ gsize i_len;
+ const gchar *psz_value = secret_value_get(p_secret_value, &i_len);
+ if (i_len > 0)
+ {
+ if (vlc_keystore_entry_set_secret(p_entry,
+ (const uint8_t *)psz_value, i_len))
+ {
+ secret_value_unref(p_secret_value);
+ goto error;
+ }
+ }
+ secret_value_unref(p_secret_value);
+ }
+ g_list_free_full(p_list, g_object_unref);
+ *pp_entries = p_entries;
+ return i_entry_count;
+
+error:
+ g_list_free_full(p_list, g_object_unref);
+ if (i_entry_count > 0)
+ vlc_keystore_release_entries(p_entries, i_entry_count);
+ return 0;
+}
+
+static unsigned int
+Remove(vlc_keystore *p_keystore, const char *const ppsz_values[KEY_MAX])
+{
+ SecretService *p_ss = (SecretService *) p_keystore->p_sys;
+
+ GList *p_list = items_search(p_ss, ppsz_values, true);
+ if (!p_list)
+ return 0;
+
+ unsigned int i_entry_count = 0;
+ for (GList *l = p_list; l != NULL; l = l->next)
+ {
+ SecretItem *p_item = (SecretItem *) l->data;
+ secret_item_delete(p_item, NULL, NULL, NULL);
+ i_entry_count++;
+ }
+ g_list_free_full(p_list, g_object_unref);
+ return i_entry_count;
+}
+
+struct secrets_watch_data
+{
+ vlc_sem_t sem;
+ bool b_running;
+};
+
+static void
+dbus_appeared_cb(GDBusConnection *connection, const gchar *name,
+ const gchar *name_owner, gpointer user_data)
+{
+ (void) connection; (void) name; (void)name_owner;
+ struct secrets_watch_data *p_watch_data = user_data;
+ p_watch_data->b_running = true;
+ vlc_sem_post(&p_watch_data->sem);
+}
+
+static void
+dbus_vanished_cb(GDBusConnection *connection, const gchar *name,
+ gpointer user_data)
+{
+ (void) connection; (void) name;
+ struct secrets_watch_data *p_watch_data = user_data;
+ p_watch_data->b_running = false;
+ vlc_sem_post(&p_watch_data->sem);
+}
+
+static int
+Open(vlc_object_t *p_this)
+{
+ if (!p_this->b_force)
+ {
+ /* First, check if secrets service is running using g_bus_watch_name().
+ * Indeed, secret_service_get_sync will spawn a service if it's not
+ * running, even on non Gnome environments */
+ struct secrets_watch_data watch_data;
+ watch_data.b_running = false;
+ vlc_sem_init(&watch_data.sem, 0);
+
+ guint i_id = g_bus_watch_name(G_BUS_TYPE_SESSION,
+ "org.freedesktop.secrets",
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ dbus_appeared_cb, dbus_vanished_cb,
+ &watch_data, NULL);
+
+ /* We are guaranteed that one of the callbacks will be invoked after
+ * calling g_bus_watch_name */
+ vlc_sem_wait_i11e(&watch_data.sem);
+
+ g_bus_unwatch_name(i_id);
+ vlc_sem_destroy(&watch_data.sem);
+
+ if (!watch_data.b_running)
+ return VLC_EGENERIC;
+ }
+
+ GCancellable *p_canc = cancellable_register();
+ SecretService *p_ss = secret_service_get_sync(SECRET_SERVICE_NONE,
+ p_canc, NULL);
+ cancellable_unregister(p_canc);
+ if (!p_ss)
+ return VLC_EGENERIC;
+
+ vlc_keystore *p_keystore = (vlc_keystore *)p_this;
+
+ p_keystore->p_sys = (vlc_keystore_sys *) p_ss;
+ p_keystore->pf_store = Store;
+ p_keystore->pf_find = Find;
+ p_keystore->pf_remove = Remove;
+
+ return VLC_SUCCESS;
+}
+
+static void
+Close(vlc_object_t *p_this)
+{
+ vlc_keystore *p_keystore = (vlc_keystore *)p_this;
+ SecretService *p_ss = (SecretService *) p_keystore->p_sys;
+ g_object_unref(p_ss);
+ secret_service_disconnect();
+}
--
2.1.4
More information about the vlc-devel
mailing list