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 *****************************************************************************/
25 #include <vlc_common.h>
26 #include <vlc_plugin.h>
27 #include <vlc_keystore.h>
28 #include <vlc_interrupt.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
*);
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()
49 static const char *const ppsz_keys
[] = {
58 static_assert(sizeof(ppsz_keys
)/sizeof(*ppsz_keys
) == KEY_MAX
, "key mismatch");
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)
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();
82 vlc_interrupt_register(cancellable_interrupted
, p_canc
);
86 static void cancellable_unregister(GCancellable
*p_canc
)
90 vlc_interrupt_unregister();
91 g_object_unref(p_canc
);
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
,
102 for (unsigned int i
= 0; i
< KEY_MAX
; ++i
)
105 g_hash_table_insert(p_hash
, (gpointer
) ppsz_keys
[i
],
106 (gpointer
) ppsz_values
[i
]);
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
)
121 ppsz_values
[i_key
] = strdup((const char *)value
);
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
);
132 ghashtable_insert_vlc_id(GHashTable
*g_hash
)
134 g_hash_table_insert(g_hash
, (gpointer
) ".created_by",
135 (gpointer
) VLC_KEYSTORE_NAME
);
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
);
146 ghashtable_insert_vlc_id(p_hash
);
148 SecretValue
*p_sv
= secret_value_new((const gchar
*)p_secret
, i_secret_len
,
152 g_hash_table_unref(p_hash
);
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
;
168 items_search(SecretService
*p_ss
, const char *const ppsz_values
[KEY_MAX
],
171 GHashTable
*p_hash
= values_to_ghashtable(ppsz_values
);
175 /* If true, do not allow to remove non VLC entries */
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
,
182 | SECRET_SEARCH_UNLOCK
183 | SECRET_SEARCH_LOAD_SECRETS
,
185 cancellable_unregister(p_canc
);
186 g_hash_table_unref(p_hash
);
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);
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
));
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
);
219 g_hash_table_unref(p_attrs
);
222 SecretValue
*p_secret_value
= secret_item_get_secret(p_item
);
224 const gchar
*psz_value
= secret_value_get(p_secret_value
, &i_len
);
227 if (vlc_keystore_entry_set_secret(p_entry
,
228 (const uint8_t *)psz_value
, i_len
))
230 secret_value_unref(p_secret_value
);
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
;
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
);
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);
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
);
263 g_list_free_full(p_list
, g_object_unref
);
264 return i_entry_count
;
267 struct secrets_watch_data
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
);
284 dbus_vanished_cb(GDBusConnection
*connection
, const gchar
*name
,
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
);
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
,
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
)
322 GCancellable
*p_canc
= cancellable_register();
323 SecretService
*p_ss
= secret_service_get_sync(SECRET_SERVICE_NONE
,
325 cancellable_unregister(p_canc
);
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
;
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();