contrib: soxr: fix build on WIN32
[vlc.git] / modules / keystore / secret.c
blobd4d663d5c96dfe884ce24f8044c39dbcba6f96e8
1 /*****************************************************************************
2 * secret.c: libsecret keystore module
3 *****************************************************************************
4 * Copyright © 2015-2016 VLC authors, VideoLAN and VideoLabs
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
25 #include <vlc_common.h>
26 #include <vlc_plugin.h>
27 #include <vlc_keystore.h>
28 #include <vlc_interrupt.h>
30 #include <assert.h>
32 #include <libsecret/secret.h>
33 #include <gio/gdbusnamewatching.h>
35 static int Open(vlc_object_t *);
36 static void Close(vlc_object_t *);
38 vlc_module_begin()
39 set_shortname(N_("libsecret keystore"))
40 set_description(N_("Secrets are stored via libsecret"))
41 set_category(CAT_ADVANCED)
42 set_subcategory(SUBCAT_ADVANCED_MISC)
43 set_capability("keystore", 100)
44 set_callbacks(Open, Close)
45 /* Since we can't destroy gdbus_shared_thread_func */
46 cannot_unload_broken_library()
47 vlc_module_end ()
49 static const char *const ppsz_keys[] = {
50 "protocol",
51 "user",
52 "server",
53 "path",
54 "port",
55 "realm",
56 "authtype",
58 static_assert(sizeof(ppsz_keys)/sizeof(*ppsz_keys) == KEY_MAX, "key mismatch");
60 static int
61 str2key(const char *psz_key)
63 for (unsigned int i = 0; i < KEY_MAX; ++i)
65 if (strcmp(ppsz_keys[i], psz_key) == 0)
66 return i;
68 return -1;
71 static void cancellable_interrupted(void *p_data)
73 GCancellable *p_canc = p_data;
74 g_cancellable_cancel(p_canc);
77 static GCancellable *cancellable_register()
79 GCancellable *p_canc = g_cancellable_new();
80 if (!p_canc)
81 return NULL;
82 vlc_interrupt_register(cancellable_interrupted, p_canc);
83 return p_canc;
86 static void cancellable_unregister(GCancellable *p_canc)
88 if (p_canc != NULL)
90 vlc_interrupt_unregister();
91 g_object_unref(p_canc);
95 static GHashTable *
96 values_to_ghashtable(const char *const ppsz_values[KEY_MAX])
98 GHashTable *p_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
99 NULL, NULL);
100 if (!p_hash)
101 return NULL;
102 for (unsigned int i = 0; i < KEY_MAX; ++i)
104 if (ppsz_values[i])
105 g_hash_table_insert(p_hash, (gpointer) ppsz_keys[i],
106 (gpointer) ppsz_values[i]);
108 return p_hash;
111 static void
112 ghash_to_value(gpointer key, gpointer value, gpointer user_data)
114 const char **ppsz_values = user_data;
116 const char *psz_key = key;
117 int i_key = str2key(psz_key);
118 if (i_key == -1 || i_key >= KEY_MAX)
119 return;
121 ppsz_values[i_key] = strdup((const char *)value);
124 static int
125 ghashtable_to_values(GHashTable *g_hash, const char *ppsz_values[KEY_MAX])
127 g_hash_table_foreach(g_hash, ghash_to_value, ppsz_values);
128 return VLC_SUCCESS;
131 static void
132 ghashtable_insert_vlc_id(GHashTable *g_hash)
134 g_hash_table_insert(g_hash, (gpointer) ".created_by",
135 (gpointer) VLC_KEYSTORE_NAME);
138 static int
139 Store(vlc_keystore *p_keystore, const char *const ppsz_values[KEY_MAX],
140 const uint8_t *p_secret, size_t i_secret_len, const char *psz_label)
142 SecretService *p_ss = (SecretService *) p_keystore->p_sys;
143 GHashTable *p_hash = values_to_ghashtable(ppsz_values);
144 if (!p_hash)
145 return VLC_EGENERIC;
146 ghashtable_insert_vlc_id(p_hash);
148 SecretValue *p_sv = secret_value_new((const gchar *)p_secret, i_secret_len,
149 "text/plain");
150 if (!p_sv)
152 g_hash_table_unref(p_hash);
153 return VLC_EGENERIC;
156 GCancellable *p_canc = cancellable_register();
157 gboolean b_ret = secret_service_store_sync(p_ss, NULL, p_hash,
158 SECRET_COLLECTION_DEFAULT,
159 psz_label, p_sv, p_canc, NULL);
160 cancellable_unregister(p_canc);
162 secret_value_unref(p_sv);
163 g_hash_table_unref(p_hash);
164 return b_ret ? VLC_SUCCESS : VLC_EGENERIC;
167 static GList*
168 items_search(SecretService *p_ss, const char *const ppsz_values[KEY_MAX],
169 bool b_safe)
171 GHashTable *p_hash = values_to_ghashtable(ppsz_values);
172 if (!p_hash)
173 return 0;
175 /* If true, do not allow to remove non VLC entries */
176 if (b_safe)
177 ghashtable_insert_vlc_id(p_hash);
179 GCancellable *p_canc = cancellable_register();
180 GList *p_list = secret_service_search_sync(p_ss, NULL, p_hash,
181 SECRET_SEARCH_ALL
182 | SECRET_SEARCH_UNLOCK
183 | SECRET_SEARCH_LOAD_SECRETS,
184 p_canc, NULL);
185 cancellable_unregister(p_canc);
186 g_hash_table_unref(p_hash);
187 return p_list;
190 static unsigned int
191 Find(vlc_keystore *p_keystore, const char *const ppsz_values[KEY_MAX],
192 vlc_keystore_entry **pp_entries)
194 SecretService *p_ss = (SecretService *) p_keystore->p_sys;
196 GList *p_list = items_search(p_ss, ppsz_values, false);
197 if (!p_list)
198 return 0;
200 unsigned int i_found_count = g_list_length(p_list);
201 unsigned int i_entry_count = 0;
202 vlc_keystore_entry *p_entries = calloc(i_found_count,
203 sizeof(vlc_keystore_entry));
204 if (!p_entries)
205 goto error;
207 for (GList *l = p_list; l != NULL; l = l->next)
209 SecretItem *p_item = (SecretItem *) l->data;
210 GHashTable *p_attrs = secret_item_get_attributes(p_item);
212 vlc_keystore_entry *p_entry = &p_entries[i_entry_count++];
213 /* fill ppsz_values */
214 if (ghashtable_to_values(p_attrs, (const char **) p_entry->ppsz_values))
216 g_hash_table_unref(p_attrs);
217 goto error;
219 g_hash_table_unref(p_attrs);
221 /* fill secret */
222 SecretValue *p_secret_value = secret_item_get_secret(p_item);
223 gsize i_len;
224 const gchar *psz_value = secret_value_get(p_secret_value, &i_len);
225 if (i_len > 0)
227 if (vlc_keystore_entry_set_secret(p_entry,
228 (const uint8_t *)psz_value, i_len))
230 secret_value_unref(p_secret_value);
231 goto error;
234 secret_value_unref(p_secret_value);
236 g_list_free_full(p_list, g_object_unref);
237 *pp_entries = p_entries;
238 return i_entry_count;
240 error:
241 g_list_free_full(p_list, g_object_unref);
242 if (i_entry_count > 0)
243 vlc_keystore_release_entries(p_entries, i_entry_count);
244 return 0;
247 static unsigned int
248 Remove(vlc_keystore *p_keystore, const char *const ppsz_values[KEY_MAX])
250 SecretService *p_ss = (SecretService *) p_keystore->p_sys;
252 GList *p_list = items_search(p_ss, ppsz_values, true);
253 if (!p_list)
254 return 0;
256 unsigned int i_entry_count = 0;
257 for (GList *l = p_list; l != NULL; l = l->next)
259 SecretItem *p_item = (SecretItem *) l->data;
260 secret_item_delete(p_item, NULL, NULL, NULL);
261 i_entry_count++;
263 g_list_free_full(p_list, g_object_unref);
264 return i_entry_count;
267 struct secrets_watch_data
269 vlc_sem_t sem;
270 bool b_running;
273 static void
274 dbus_appeared_cb(GDBusConnection *connection, const gchar *name,
275 const gchar *name_owner, gpointer user_data)
277 (void) connection; (void) name; (void)name_owner;
278 struct secrets_watch_data *p_watch_data = user_data;
279 p_watch_data->b_running = true;
280 vlc_sem_post(&p_watch_data->sem);
283 static void
284 dbus_vanished_cb(GDBusConnection *connection, const gchar *name,
285 gpointer user_data)
287 (void) connection; (void) name;
288 struct secrets_watch_data *p_watch_data = user_data;
289 p_watch_data->b_running = false;
290 vlc_sem_post(&p_watch_data->sem);
293 static int
294 Open(vlc_object_t *p_this)
296 if (!p_this->obj.force)
298 /* First, check if secrets service is running using g_bus_watch_name().
299 * Indeed, secret_service_get_sync will spawn a service if it's not
300 * running, even on non Gnome environments */
301 struct secrets_watch_data watch_data;
302 watch_data.b_running = false;
303 vlc_sem_init(&watch_data.sem, 0);
305 guint i_id = g_bus_watch_name(G_BUS_TYPE_SESSION,
306 "org.freedesktop.secrets",
307 G_BUS_NAME_WATCHER_FLAGS_NONE,
308 dbus_appeared_cb, dbus_vanished_cb,
309 &watch_data, NULL);
311 /* We are guaranteed that one of the callbacks will be invoked after
312 * calling g_bus_watch_name */
313 vlc_sem_wait_i11e(&watch_data.sem);
315 g_bus_unwatch_name(i_id);
316 vlc_sem_destroy(&watch_data.sem);
318 if (!watch_data.b_running)
319 return VLC_EGENERIC;
322 GCancellable *p_canc = cancellable_register();
323 SecretService *p_ss = secret_service_get_sync(SECRET_SERVICE_NONE,
324 p_canc, NULL);
325 cancellable_unregister(p_canc);
326 if (!p_ss)
327 return VLC_EGENERIC;
329 vlc_keystore *p_keystore = (vlc_keystore *)p_this;
331 p_keystore->p_sys = (vlc_keystore_sys *) p_ss;
332 p_keystore->pf_store = Store;
333 p_keystore->pf_find = Find;
334 p_keystore->pf_remove = Remove;
336 return VLC_SUCCESS;
339 static void
340 Close(vlc_object_t *p_this)
342 vlc_keystore *p_keystore = (vlc_keystore *)p_this;
343 SecretService *p_ss = (SecretService *) p_keystore->p_sys;
344 g_object_unref(p_ss);
345 secret_service_disconnect();