1 /* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright (C) 2008 Carlos Garcia Campos
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 *
20 * Author: Carlos Garcia Campos <carlosgc@gnome.org>
21 */
22
23 #include <config.h>
24
25 #define SECRET_API_SUBJECT_TO_CHANGE 1
26
27 #ifdef HAVE_KEYRING
28 #include <libsecret/secret.h>
29 #endif
30
31 #include "gvfskeyring.h"
32
33 gboolean
g_vfs_keyring_is_available(void)34 g_vfs_keyring_is_available (void)
35 {
36 #ifdef HAVE_KEYRING
37 return TRUE;
38 #else
39 return FALSE;
40 #endif
41 }
42
43 #ifdef HAVE_KEYRING
44
45 static void
insert_string(const gchar * key,const gchar * value,GHashTable ** attributes)46 insert_string (const gchar *key,
47 const gchar *value,
48 GHashTable **attributes)
49 {
50 if (*attributes == NULL)
51 return;
52
53 if (!g_utf8_validate (value, -1, NULL))
54 {
55 g_warning ("Non-utf8 value for key %s\n", key);
56 g_hash_table_unref (*attributes);
57 *attributes = NULL;
58 }
59
60 g_hash_table_insert (*attributes,
61 g_strdup (key),
62 g_strdup (value));
63 }
64
65 static void
insert_int(const gchar * key,gint value,GHashTable ** attributes)66 insert_int (const gchar *key,
67 gint value,
68 GHashTable **attributes)
69 {
70 if (*attributes == NULL)
71 return;
72
73 g_hash_table_insert (*attributes,
74 g_strdup (key),
75 g_strdup_printf ("%d", value));
76 }
77
78 static GHashTable *
build_network_attributes(const gchar * username,const gchar * host,const gchar * domain,const gchar * protocol,const gchar * object,const gchar * authtype,guint32 port)79 build_network_attributes (const gchar *username,
80 const gchar *host,
81 const gchar *domain,
82 const gchar *protocol,
83 const gchar *object,
84 const gchar *authtype,
85 guint32 port)
86 {
87 GHashTable *attributes;
88
89 attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
90
91 if (username)
92 insert_string ("user", username, &attributes);
93 if (host)
94 insert_string ("server", host, &attributes);
95 if (domain)
96 insert_string ("domain", domain, &attributes);
97 if (protocol)
98 insert_string ("protocol", protocol, &attributes);
99 if (object)
100 insert_string ("object", object, &attributes);
101 if (authtype)
102 insert_string ("authtype", authtype, &attributes);
103 if (port != 0)
104 insert_int ("port", (gint)port, &attributes);
105
106 return attributes;
107 }
108
109 static gchar *
build_network_label(const gchar * user,const gchar * server,const gchar * object,guint32 port)110 build_network_label (const gchar *user,
111 const gchar *server,
112 const gchar *object,
113 guint32 port)
114 {
115 GString *s;
116 gchar *name;
117
118 if (server != NULL)
119 {
120 s = g_string_new (NULL);
121 if (user != NULL)
122 {
123 g_string_append_uri_escaped (s, user, G_URI_RESERVED_CHARS_ALLOWED_IN_USERINFO, TRUE);
124 g_string_append (s, "@");
125 }
126 g_string_append (s, server);
127 if (port != 0)
128 g_string_append_printf (s, ":%d", port);
129 if (object != NULL)
130 g_string_append_printf (s, "/%s", object);
131 name = g_string_free (s, FALSE);
132 }
133 else
134 {
135 name = g_strdup ("network password");
136 }
137 return name;
138 }
139
140 static gint
compare_specificity(gconstpointer a,gconstpointer b)141 compare_specificity (gconstpointer a,
142 gconstpointer b)
143 {
144 GHashTable *attributes_a, *attributes_b;
145 SecretItem *item_a, *item_b;
146 int res;
147
148 item_a = SECRET_ITEM (a);
149 attributes_a = secret_item_get_attributes (item_a);
150
151 item_b = SECRET_ITEM (b);
152 attributes_b = secret_item_get_attributes (item_b);
153
154 res = g_hash_table_size (attributes_a) - g_hash_table_size (attributes_b);
155
156 /* Prefer the most recent item if they are equal in specificity. */
157 if (res == 0)
158 res = secret_item_get_modified (item_b) - secret_item_get_modified (item_a);
159
160 g_hash_table_unref (attributes_a);
161 g_hash_table_unref (attributes_b);
162
163 return res;
164 }
165
166 #endif /* HAVE_KEYRING */
167
168 gboolean
g_vfs_keyring_lookup_password(const gchar * username,const gchar * host,const gchar * domain,const gchar * protocol,const gchar * object,const gchar * authtype,guint32 port,gchar ** username_out,gchar ** domain_out,gchar ** password_out)169 g_vfs_keyring_lookup_password (const gchar *username,
170 const gchar *host,
171 const gchar *domain,
172 const gchar *protocol,
173 const gchar *object,
174 const gchar *authtype,
175 guint32 port,
176 gchar **username_out,
177 gchar **domain_out,
178 gchar **password_out)
179 {
180 #ifdef HAVE_KEYRING
181 GHashTable *attributes;
182 SecretItem *item;
183 SecretValue *secret;
184 GList *plist;
185 GError *error = NULL;
186
187
188 attributes = build_network_attributes (username, host, domain, protocol, object, authtype, port);
189 plist = secret_service_search_sync (NULL, SECRET_SCHEMA_COMPAT_NETWORK, attributes,
190 SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS |
191 SECRET_SEARCH_ALL,
192 NULL, &error);
193 g_hash_table_unref (attributes);
194
195 if (error != NULL)
196 {
197 g_error_free (error);
198 return FALSE;
199 }
200
201 if (plist == NULL)
202 return FALSE;
203
204 /* We want the least specific result, so we sort the return values.
205 For instance, given both items for ftp://host:port and ftp://host
206 in the keyring we always want to use the ftp://host one for
207 i.e. ftp://host/some/path. */
208
209 plist = g_list_sort (plist, compare_specificity);
210
211 item = SECRET_ITEM (plist->data);
212 secret = secret_item_get_secret (item);
213 attributes = secret_item_get_attributes (item);
214 g_list_free_full (plist, g_object_unref);
215
216 if (secret == NULL)
217 {
218 if (attributes)
219 g_hash_table_unref (attributes);
220 return FALSE;
221 }
222
223 *password_out = g_strdup (secret_value_get (secret, NULL));
224 secret_value_unref (secret);
225
226 if (username_out)
227 *username_out = g_strdup (g_hash_table_lookup (attributes, "user"));
228
229 if (domain_out)
230 *domain_out = g_strdup (g_hash_table_lookup (attributes, "domain"));
231
232 g_hash_table_unref (attributes);
233 return TRUE;
234 #else
235 return FALSE;
236 #endif /* HAVE_KEYRING */
237 }
238
239 gboolean
g_vfs_keyring_save_password(const gchar * username,const gchar * host,const gchar * domain,const gchar * protocol,const gchar * object,const gchar * authtype,guint32 port,const gchar * password,GPasswordSave flags)240 g_vfs_keyring_save_password (const gchar *username,
241 const gchar *host,
242 const gchar *domain,
243 const gchar *protocol,
244 const gchar *object,
245 const gchar *authtype,
246 guint32 port,
247 const gchar *password,
248 GPasswordSave flags)
249 {
250 #ifdef HAVE_KEYRING
251 const gchar *keyring;
252 GHashTable *attributes;
253 gchar *label;
254 gboolean ret;
255
256 if (flags == G_PASSWORD_SAVE_NEVER)
257 return FALSE;
258
259 keyring = (flags == G_PASSWORD_SAVE_FOR_SESSION) ? SECRET_COLLECTION_SESSION : SECRET_COLLECTION_DEFAULT;
260
261 label = build_network_label (username, host, object, port);
262 attributes = build_network_attributes (username, host, domain, protocol, object, authtype, port);
263
264 ret = secret_password_storev_sync (SECRET_SCHEMA_COMPAT_NETWORK, attributes,
265 keyring, label, password, NULL, NULL);
266
267 g_free (label);
268 g_hash_table_unref (attributes);
269
270 return ret;
271 #else
272 return FALSE;
273 #endif /* HAVE_KEYRING */
274 }
275