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