1 /*
2 * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
3 *
4 * This library is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18 /**
19 * SECTION: e-secret-store
20 * @include: libedataserver/libedataserver.h
21 * @short_description: Interface to store secrets
22 *
23 * The e-secret-store API provides an interface to store,
24 * lookup and delete secrets from the keyring.
25 **/
26
27 #include "evolution-data-server-config.h"
28
29 #include <glib.h>
30
31 #ifdef G_OS_WIN32
32 #include <string.h>
33 #include <errno.h>
34 #else
35 #include <libsecret/secret.h>
36 #endif
37
38 #include "e-data-server-util.h"
39 #include "e-secret-store.h"
40
41 #ifdef G_OS_WIN32
42
43 G_LOCK_DEFINE_STATIC (secrets_file);
44 static GHashTable *session_secrets = NULL;
45 #define SECRETS_SECTION "Secrets"
46
47 static gchar *
encode_secret(const gchar * secret)48 encode_secret (const gchar *secret)
49 {
50 return g_base64_encode ((const guchar *) secret, strlen (secret));
51 }
52
53 static gchar *
decode_secret(const gchar * secret)54 decode_secret (const gchar *secret)
55 {
56 guchar *decoded;
57 gchar *tmp;
58 gsize len = 0;
59
60 decoded = g_base64_decode (secret, &len);
61 if (!decoded || !len) {
62 g_free (decoded);
63 return NULL;
64 }
65
66 tmp = g_strndup ((const gchar *) decoded, len);
67 g_free (decoded);
68
69 return tmp;
70 }
71
72 static gchar *
get_secrets_filename(void)73 get_secrets_filename (void)
74 {
75 return g_build_filename (e_get_user_config_dir (), "secrets", NULL);
76 }
77
78 static GKeyFile *
read_secrets_file(GError ** error)79 read_secrets_file (GError **error)
80 {
81 gchar *filename;
82 GKeyFile *secrets;
83
84 secrets = g_key_file_new ();
85
86 filename = get_secrets_filename ();
87
88 if (g_file_test (filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
89 if (!g_key_file_load_from_file (secrets, filename, G_KEY_FILE_NONE, error)) {
90 g_key_file_free (secrets);
91 secrets = NULL;
92 }
93 }
94
95 g_free (filename);
96
97 return secrets;
98 }
99
100 static gboolean
store_secrets_file(GKeyFile * secrets,GError ** error)101 store_secrets_file (GKeyFile *secrets,
102 GError **error)
103 {
104 gchar *content, *filename;
105 gsize length;
106 gboolean success;
107
108 g_return_val_if_fail (secrets != NULL, FALSE);
109
110 if (!g_file_test (e_get_user_config_dir (), G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
111 if (g_mkdir_with_parents (e_get_user_config_dir (), 0700) == -1) {
112 g_set_error_literal (
113 error, G_FILE_ERROR,
114 g_file_error_from_errno (errno),
115 g_strerror (errno));
116 return FALSE;
117 }
118 }
119
120 content = g_key_file_to_data (secrets, &length, error);
121 if (!content)
122 return FALSE;
123
124
125 filename = get_secrets_filename ();
126
127 success = g_file_set_contents (filename, content, length, error);
128
129 g_free (filename);
130 g_free (content);
131
132 return success;
133 }
134
135 static gboolean
e_win32_secret_store_secret_sync(const gchar * uid,const gchar * secret,gboolean permanently,GError ** error)136 e_win32_secret_store_secret_sync (const gchar *uid,
137 const gchar *secret,
138 gboolean permanently,
139 GError **error)
140 {
141 GKeyFile *secrets;
142 gboolean success;
143
144 g_return_val_if_fail (uid != NULL, FALSE);
145
146 G_LOCK (secrets_file);
147
148 if (permanently) {
149 secrets = read_secrets_file (error);
150 success = secrets != NULL;
151
152 if (secrets) {
153 gchar *encoded;
154
155 encoded = secret && *secret ? encode_secret (secret) : g_strdup (secret);
156
157 g_key_file_set_string (secrets, SECRETS_SECTION, uid, encoded);
158
159 success = store_secrets_file (secrets, error);
160
161 g_key_file_free (secrets);
162 g_free (encoded);
163 }
164 } else {
165 gchar *encoded;
166
167 if (!session_secrets)
168 session_secrets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) e_util_safe_free_string);
169
170 encoded = secret && *secret ? encode_secret (secret) : g_strdup (secret);
171 if (!encoded)
172 g_hash_table_remove (session_secrets, uid);
173 else
174 g_hash_table_insert (session_secrets, g_strdup (uid), encoded);
175 }
176
177 G_UNLOCK (secrets_file);
178
179 return success;
180 }
181
182 static gchar *
e_win32_secret_lookup_secret_sync(const gchar * uid,GError ** error)183 e_win32_secret_lookup_secret_sync (const gchar *uid,
184 GError **error)
185 {
186 GKeyFile *secrets;
187 gchar *secret = NULL;
188
189 g_return_val_if_fail (uid != NULL, NULL);
190
191 G_LOCK (secrets_file);
192
193 if (session_secrets) {
194 const gchar *encoded;
195
196 encoded = g_hash_table_lookup (session_secrets, uid);
197 if (encoded)
198 secret = decode_secret (encoded);
199 }
200
201 if (!secret) {
202 secrets = read_secrets_file (error);
203 if (secrets) {
204 gchar *tmp;
205
206 tmp = g_key_file_get_string (secrets, SECRETS_SECTION, uid, NULL);
207 if (tmp) {
208 secret = *tmp ? decode_secret (tmp) : g_strdup ("");
209 g_free (tmp);
210 }
211
212 g_key_file_free (secrets);
213 }
214 }
215
216 G_UNLOCK (secrets_file);
217
218 return secret;
219 }
220
221 static gboolean
e_win32_secret_delete_secret_sync(const gchar * uid,GError ** error)222 e_win32_secret_delete_secret_sync (const gchar *uid,
223 GError **error)
224 {
225 GKeyFile *secrets;
226 gboolean success = FALSE;
227
228 g_return_val_if_fail (uid != NULL, FALSE);
229
230 G_LOCK (secrets_file);
231
232 if (session_secrets) {
233 success = g_hash_table_remove (session_secrets, uid);
234 }
235
236 secrets = read_secrets_file (error);
237 if (secrets) {
238 success = TRUE;
239
240 if (g_key_file_remove_key (secrets, SECRETS_SECTION, uid, NULL)) {
241 success = store_secrets_file (secrets, error);
242 }
243
244 g_key_file_free (secrets);
245 }
246
247 G_UNLOCK (secrets_file);
248
249 return success;
250 }
251
252 #else /* G_OS_WIN32 */
253
254 #define KEYRING_ITEM_ATTRIBUTE_UID "e-source-uid"
255 #define KEYRING_ITEM_ATTRIBUTE_ORIGIN "eds-origin"
256
257 #ifdef DBUS_SERVICES_PREFIX
258 #define ORIGIN_KEY DBUS_SERVICES_PREFIX "." PACKAGE
259 #else
260 #define ORIGIN_KEY PACKAGE
261 #endif
262
263 static SecretSchema password_schema = {
264 "org.gnome.Evolution.Data.Source",
265 SECRET_SCHEMA_DONT_MATCH_NAME,
266 {
267 { KEYRING_ITEM_ATTRIBUTE_UID, SECRET_SCHEMA_ATTRIBUTE_STRING },
268 { KEYRING_ITEM_ATTRIBUTE_ORIGIN, SECRET_SCHEMA_ATTRIBUTE_STRING },
269 { NULL, 0 }
270 }
271 };
272
273 #endif /* G_OS_WIN32 */
274
275 /**
276 * e_secret_store_store_sync:
277 * @uid: a unique identifier of the secret
278 * @secret: the secret to store
279 * @label: human readable description of the secret
280 * @permanently: store permanently or just for the session
281 * @cancellable: optional #GCancellable object, or %NULL
282 * @error: return location for a #GError, or %NULL
283 *
284 * Stores the @secret for the @uid.
285 *
286 * If @permanently is %TRUE, the secret is stored in the default keyring.
287 * Otherwise the secret is stored in the memory-only session keyring. If
288 * an error occurs, the function sets @error and returns %FALSE.
289 *
290 * Returns: %TRUE on success, %FALSE on error
291 *
292 * Since: 3.18
293 **/
294 gboolean
e_secret_store_store_sync(const gchar * uid,const gchar * secret,const gchar * label,gboolean permanently,GCancellable * cancellable,GError ** error)295 e_secret_store_store_sync (const gchar *uid,
296 const gchar *secret,
297 const gchar *label,
298 gboolean permanently,
299 GCancellable *cancellable,
300 GError **error)
301 {
302 gboolean success;
303 #ifndef G_OS_WIN32
304 const gchar *collection;
305 #endif
306
307 g_return_val_if_fail (uid != NULL, FALSE);
308 g_return_val_if_fail (secret != NULL, FALSE);
309
310 #ifndef G_OS_WIN32
311 if (permanently)
312 collection = SECRET_COLLECTION_DEFAULT;
313 else
314 collection = SECRET_COLLECTION_SESSION;
315 #endif
316
317 #ifdef G_OS_WIN32
318 success = e_win32_secret_store_secret_sync (uid, secret, permanently, error);
319 #else
320 success = secret_password_store_sync (
321 &password_schema,
322 collection, label, secret,
323 cancellable, error,
324 KEYRING_ITEM_ATTRIBUTE_UID, uid,
325 KEYRING_ITEM_ATTRIBUTE_ORIGIN, ORIGIN_KEY,
326 NULL);
327 #endif
328
329 return success;
330 }
331
332 /**
333 * e_secret_store_lookup_sync:
334 * @uid: a unique identifier of the secret
335 * @out_secret: (out): return location for the secret, or %NULL
336 * @cancellable: optional #GCancellable object, or %NULL
337 * @error: return location for a #GError, or %NULL
338 *
339 * Looks up a secret for the @uid. Both the default and session keyrings
340 * are queried.
341 *
342 * Note the boolean return value indicates whether the lookup operation
343 * itself completed successfully, not whether the secret was found. If
344 * no secret was found, the function will set @out_secret to %NULL,
345 * but still return %TRUE. If an error occurs, the function sets @error
346 * and returns %FALSE.
347 *
348 * Returns: %TRUE on success, %FALSE on error
349 *
350 * Since: 3.18
351 **/
352 gboolean
e_secret_store_lookup_sync(const gchar * uid,gchar ** out_secret,GCancellable * cancellable,GError ** error)353 e_secret_store_lookup_sync (const gchar *uid,
354 gchar **out_secret,
355 GCancellable *cancellable,
356 GError **error)
357 {
358 gchar *temp = NULL;
359 gboolean success = TRUE;
360 GError *local_error = NULL;
361
362 g_return_val_if_fail (uid != NULL, FALSE);
363
364 #ifdef G_OS_WIN32
365 temp = e_win32_secret_lookup_secret_sync (uid, &local_error);
366 #else
367 temp = secret_password_lookup_sync (
368 &password_schema,
369 cancellable, &local_error,
370 KEYRING_ITEM_ATTRIBUTE_UID, uid,
371 NULL);
372 #endif
373
374 if (local_error != NULL) {
375 g_warn_if_fail (temp == NULL);
376 g_propagate_error (error, local_error);
377 success = FALSE;
378 } else if (out_secret != NULL) {
379 *out_secret = temp; /* takes ownership */
380 } else {
381 e_util_safe_free_string (temp);
382 }
383
384 return success;
385 }
386
387 /**
388 * e_secret_store_delete_sync:
389 * @uid: a unique identifier of the secret
390 * @cancellable: optional #GCancellable object, or %NULL
391 * @error: return location for a #GError, or %NULL
392 *
393 * Deletes the secret for @uid from either the default keyring or
394 * session keyring.
395 *
396 * Note the boolean return value indicates whether the delete operation
397 * itself completed successfully, not whether the secret was found and
398 * deleted. If no such secret was found, the function will still return
399 * %TRUE. If an error occurs, the function sets @error and returns %FALSE.
400 *
401 * Returns: %TRUE on success, %FALSE on error
402 *
403 * Since: 3.18
404 **/
405 gboolean
e_secret_store_delete_sync(const gchar * uid,GCancellable * cancellable,GError ** error)406 e_secret_store_delete_sync (const gchar *uid,
407 GCancellable *cancellable,
408 GError **error)
409 {
410 gboolean success = TRUE;
411 GError *local_error = NULL;
412
413 g_return_val_if_fail (uid != NULL, FALSE);
414
415 #ifdef G_OS_WIN32
416 e_win32_secret_delete_secret_sync (uid, &local_error);
417 #else
418 /* The return value indicates whether any passwords were removed,
419 * not whether the operation completed successfully. So we have
420 * to check the GError directly. */
421 secret_password_clear_sync (
422 &password_schema,
423 cancellable, &local_error,
424 KEYRING_ITEM_ATTRIBUTE_UID, uid,
425 NULL);
426 #endif
427
428 if (local_error != NULL) {
429 g_propagate_error (error, local_error);
430 success = FALSE;
431 }
432
433 return success;
434 }
435