1 /*
2 * gnome_keyring.c: GNOME Keyring provider for SVN_AUTH_CRED_*
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24 /* ==================================================================== */
25
26
27 /*** Includes. ***/
28 #include <apr_pools.h>
29 #include <apr_strings.h>
30 #include "svn_auth.h"
31 #include "svn_hash.h"
32 #include "svn_version.h"
33 #include "private/svn_auth_private.h"
34 #include "svn_private_config.h"
35
36 #ifdef SVN_HAVE_LIBSECRET
37
38 #include <libsecret/secret.h>
39
40 /* Return TRUE if the default collection is available and FALSE
41 otherwise. In interactive mode the collection only has to exist to
42 be available, it can be locked or unlocked. The default collection
43 will be created if necessary.
44
45 In non-interactive mode the collection is only available if it
46 already exists and is unlocked. Such an available collection can
47 be used without prompting. Strictly this is racy: nothing ensures
48 the collection remains unlocked. A similar issue affects the
49 KWallet and original GNOME Keyring providers.
50
51 As a non-racy alternative one could override prompt_async in the
52 _SecretServiceClass vtable, the get/set would still fail but there
53 would be no prompt and no race. This "works" but it is not clear
54 to me whether it is legitimate since the SecretService is a
55 singleton and the effect would be application-wide.
56 */
57 static svn_boolean_t
available_collection(svn_boolean_t non_interactive,apr_pool_t * pool)58 available_collection(svn_boolean_t non_interactive,
59 apr_pool_t *pool)
60 {
61 GError *gerror = NULL;
62 SecretService *service = NULL;
63 SecretCollection *collection = NULL;
64
65 service = secret_service_get_sync(SECRET_SERVICE_NONE, NULL, &gerror);
66 if (gerror || !service)
67 goto error_return;
68
69 collection = secret_collection_for_alias_sync(service,
70 SECRET_COLLECTION_DEFAULT,
71 SECRET_COLLECTION_NONE,
72 NULL, &gerror);
73 if (gerror)
74 goto error_return;
75
76 if (!collection)
77 {
78 if (non_interactive)
79 goto error_return;
80
81 /* "Default" is the label used by the old libgnome-keyring. */
82 collection = secret_collection_create_sync(service, "Default",
83 SECRET_COLLECTION_DEFAULT,
84 0, NULL, &gerror);
85 if (gerror || !collection)
86 goto error_return;
87 }
88
89 if (non_interactive && secret_collection_get_locked(collection))
90 goto error_return;
91
92 g_object_unref(collection);
93 g_object_unref(service);
94
95 return TRUE;
96
97 error_return:
98 if (gerror)
99 g_error_free(gerror);
100 if (collection)
101 g_object_unref(collection);
102 if (service)
103 g_object_unref(service);
104 return FALSE;
105 }
106
107 /* Implementation of svn_auth__password_get_t that retrieves the password
108 using libsecret. */
109 static svn_error_t *
password_get_gnome_keyring(svn_boolean_t * done,const char ** password,apr_hash_t * creds,const char * realmstring,const char * username,apr_hash_t * parameters,svn_boolean_t non_interactive,apr_pool_t * pool)110 password_get_gnome_keyring(svn_boolean_t *done,
111 const char **password,
112 apr_hash_t *creds,
113 const char *realmstring,
114 const char *username,
115 apr_hash_t *parameters,
116 svn_boolean_t non_interactive,
117 apr_pool_t *pool)
118 {
119 GError *gerror = NULL;
120 gchar *gpassword;
121
122 *done = FALSE;
123
124 if (!available_collection(non_interactive, pool))
125 return SVN_NO_ERROR;
126
127 gpassword = secret_password_lookup_sync(SECRET_SCHEMA_COMPAT_NETWORK, NULL,
128 &gerror,
129 "domain", realmstring,
130 "user", username,
131 NULL);
132 if (gerror)
133 {
134 /* ### TODO: return or log the error? */
135 g_error_free(gerror);
136 }
137 else if (gpassword)
138 {
139 *password = apr_pstrdup(pool, gpassword);
140 g_free(gpassword);
141 *done = TRUE;
142 }
143
144 return SVN_NO_ERROR;
145 }
146
147 /* Implementation of svn_auth__password_set_t that stores the password
148 using libsecret. */
149 static svn_error_t *
password_set_gnome_keyring(svn_boolean_t * done,apr_hash_t * creds,const char * realmstring,const char * username,const char * password,apr_hash_t * parameters,svn_boolean_t non_interactive,apr_pool_t * pool)150 password_set_gnome_keyring(svn_boolean_t *done,
151 apr_hash_t *creds,
152 const char *realmstring,
153 const char *username,
154 const char *password,
155 apr_hash_t *parameters,
156 svn_boolean_t non_interactive,
157 apr_pool_t *pool)
158 {
159 GError *gerror = NULL;
160 gboolean gstatus;
161
162 *done = FALSE;
163
164 if (!available_collection(non_interactive, pool))
165 return SVN_NO_ERROR;
166
167 /* "network password" is the label used by the old libgnome-keyring. */
168 gstatus = secret_password_store_sync(SECRET_SCHEMA_COMPAT_NETWORK,
169 SECRET_COLLECTION_DEFAULT,
170 "network password",
171 password,
172 NULL, &gerror,
173 "domain", realmstring,
174 "user", username,
175 NULL);
176 if (gerror)
177 {
178 /* ### TODO: return or log the error? */
179 g_error_free(gerror);
180 }
181 else if (gstatus)
182 {
183 *done = TRUE;
184 }
185
186 return SVN_NO_ERROR;
187 }
188
189 #endif /* SVN_HAVE_LIBSECRET */
190
191 #ifdef SVN_HAVE_GNOME_KEYRING
192
193 #include <glib.h>
194 #include <gnome-keyring.h>
195
196 /* Returns the default keyring name, allocated in RESULT_POOL. */
197 static char*
get_default_keyring_name(apr_pool_t * result_pool)198 get_default_keyring_name(apr_pool_t *result_pool)
199 {
200 char *name, *def;
201 GnomeKeyringResult gkr;
202
203 gkr = gnome_keyring_get_default_keyring_sync(&name);
204 if (gkr != GNOME_KEYRING_RESULT_OK)
205 return NULL;
206
207 def = apr_pstrdup(result_pool, name);
208 g_free(name);
209
210 return def;
211 }
212
213 /* Returns TRUE if the KEYRING_NAME is locked. */
214 static svn_boolean_t
check_keyring_is_locked(const char * keyring_name)215 check_keyring_is_locked(const char *keyring_name)
216 {
217 GnomeKeyringInfo *info;
218 svn_boolean_t locked;
219 GnomeKeyringResult gkr;
220
221 gkr = gnome_keyring_get_info_sync(keyring_name, &info);
222 if (gkr != GNOME_KEYRING_RESULT_OK)
223 return FALSE;
224
225 if (gnome_keyring_info_get_is_locked(info))
226 locked = TRUE;
227 else
228 locked = FALSE;
229
230 gnome_keyring_info_free(info);
231
232 return locked;
233 }
234
235 /* Unlock the KEYRING_NAME with the KEYRING_PASSWORD. If KEYRING was
236 successfully unlocked return TRUE. */
237 static svn_boolean_t
unlock_gnome_keyring(const char * keyring_name,const char * keyring_password,apr_pool_t * pool)238 unlock_gnome_keyring(const char *keyring_name,
239 const char *keyring_password,
240 apr_pool_t *pool)
241 {
242 GnomeKeyringInfo *info;
243 GnomeKeyringResult gkr;
244
245 gkr = gnome_keyring_get_info_sync(keyring_name, &info);
246 if (gkr != GNOME_KEYRING_RESULT_OK)
247 return FALSE;
248
249 gkr = gnome_keyring_unlock_sync(keyring_name, keyring_password);
250 gnome_keyring_info_free(info);
251 if (gkr != GNOME_KEYRING_RESULT_OK)
252 return FALSE;
253
254 return check_keyring_is_locked(keyring_name);
255 }
256
257
258 /* There is a race here: this ensures keyring is unlocked just now,
259 but will it still be unlocked when we use it? */
260 static svn_error_t *
ensure_gnome_keyring_is_unlocked(svn_boolean_t non_interactive,apr_hash_t * parameters,apr_pool_t * scratch_pool)261 ensure_gnome_keyring_is_unlocked(svn_boolean_t non_interactive,
262 apr_hash_t *parameters,
263 apr_pool_t *scratch_pool)
264 {
265 const char *default_keyring = get_default_keyring_name(scratch_pool);
266
267 if (! non_interactive)
268 {
269 svn_auth_gnome_keyring_unlock_prompt_func_t unlock_prompt_func =
270 svn_hash_gets(parameters,
271 SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_FUNC);
272 void *unlock_prompt_baton =
273 svn_hash_gets(parameters,
274 SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_BATON);
275
276 char *keyring_password;
277
278 if (unlock_prompt_func && check_keyring_is_locked(default_keyring))
279 {
280 SVN_ERR((*unlock_prompt_func)(&keyring_password,
281 default_keyring,
282 unlock_prompt_baton,
283 scratch_pool));
284
285 /* If keyring is locked give up and try the next provider. */
286 if (! unlock_gnome_keyring(default_keyring, keyring_password,
287 scratch_pool))
288 return SVN_NO_ERROR;
289 }
290 }
291 else
292 {
293 if (check_keyring_is_locked(default_keyring))
294 {
295 return svn_error_create(SVN_ERR_AUTHN_CREDS_UNAVAILABLE, NULL,
296 _("GNOME Keyring is locked and "
297 "we are non-interactive"));
298 }
299 }
300
301 return SVN_NO_ERROR;
302 }
303
304 /* Implementation of svn_auth__password_get_t that retrieves the password
305 from GNOME Keyring. */
306 static svn_error_t *
password_get_gnome_keyring(svn_boolean_t * done,const char ** password,apr_hash_t * creds,const char * realmstring,const char * username,apr_hash_t * parameters,svn_boolean_t non_interactive,apr_pool_t * pool)307 password_get_gnome_keyring(svn_boolean_t *done,
308 const char **password,
309 apr_hash_t *creds,
310 const char *realmstring,
311 const char *username,
312 apr_hash_t *parameters,
313 svn_boolean_t non_interactive,
314 apr_pool_t *pool)
315 {
316 GnomeKeyringResult result;
317 GList *items;
318
319 *done = FALSE;
320
321 SVN_ERR(ensure_gnome_keyring_is_unlocked(non_interactive, parameters, pool));
322
323 if (! svn_hash_gets(parameters, "gnome-keyring-opening-failed"))
324 {
325 result = gnome_keyring_find_network_password_sync(username, realmstring,
326 NULL, NULL, NULL, NULL,
327 0, &items);
328 }
329 else
330 {
331 result = GNOME_KEYRING_RESULT_DENIED;
332 }
333
334 if (result == GNOME_KEYRING_RESULT_OK)
335 {
336 if (items && items->data)
337 {
338 GnomeKeyringNetworkPasswordData *item = items->data;
339 if (item->password)
340 {
341 size_t len = strlen(item->password);
342 if (len > 0)
343 {
344 *password = apr_pstrmemdup(pool, item->password, len);
345 *done = TRUE;
346 }
347 }
348 gnome_keyring_network_password_list_free(items);
349 }
350 }
351 else
352 {
353 svn_hash_sets(parameters, "gnome-keyring-opening-failed", "");
354 }
355
356 return SVN_NO_ERROR;
357 }
358
359 /* Implementation of svn_auth__password_set_t that stores the password in
360 GNOME Keyring. */
361 static svn_error_t *
password_set_gnome_keyring(svn_boolean_t * done,apr_hash_t * creds,const char * realmstring,const char * username,const char * password,apr_hash_t * parameters,svn_boolean_t non_interactive,apr_pool_t * pool)362 password_set_gnome_keyring(svn_boolean_t *done,
363 apr_hash_t *creds,
364 const char *realmstring,
365 const char *username,
366 const char *password,
367 apr_hash_t *parameters,
368 svn_boolean_t non_interactive,
369 apr_pool_t *pool)
370 {
371 GnomeKeyringResult result;
372 guint32 item_id;
373
374 *done = FALSE;
375
376 SVN_ERR(ensure_gnome_keyring_is_unlocked(non_interactive, parameters, pool));
377
378 if (! svn_hash_gets(parameters, "gnome-keyring-opening-failed"))
379 {
380 result = gnome_keyring_set_network_password_sync(NULL, /* default keyring */
381 username, realmstring,
382 NULL, NULL, NULL, NULL,
383 0, password,
384 &item_id);
385 }
386 else
387 {
388 result = GNOME_KEYRING_RESULT_DENIED;
389 }
390 if (result != GNOME_KEYRING_RESULT_OK)
391 {
392 svn_hash_sets(parameters, "gnome-keyring-opening-failed", "");
393 }
394
395 *done = (result == GNOME_KEYRING_RESULT_OK);
396 return SVN_NO_ERROR;
397 }
398
399 #if GLIB_CHECK_VERSION(2,6,0)
400 static void
log_noop(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)401 log_noop(const gchar *log_domain, GLogLevelFlags log_level,
402 const gchar *message, gpointer user_data)
403 {
404 /* do nothing */
405 }
406 #endif
407
408 static void
init_gnome_keyring(void)409 init_gnome_keyring(void)
410 {
411 const char *application_name = NULL;
412 application_name = g_get_application_name();
413 if (!application_name)
414 g_set_application_name("Subversion");
415
416 /* Ideally we call g_log_set_handler() with a log_domain specific to
417 libgnome-keyring. Unfortunately, at least as of gnome-keyring
418 2.22.3, it doesn't have its own log_domain. As a result, we
419 suppress stderr spam for not only libgnome-keyring, but for
420 anything else the app is linked to that uses glib logging and
421 doesn't specify a log_domain. */
422 #if GLIB_CHECK_VERSION(2,6,0)
423 g_log_set_default_handler(log_noop, NULL);
424 #endif
425 }
426
427 #endif /* SVN_HAVE_GNOME_KEYRING */
428
429
430 /*-----------------------------------------------------------------------*/
431 /* GNOME Keyring simple provider, puts passwords in GNOME Keyring */
432 /*-----------------------------------------------------------------------*/
433
434 /* Get cached encrypted credentials from the simple provider's cache. */
435 static svn_error_t *
simple_gnome_keyring_first_creds(void ** credentials,void ** iter_baton,void * provider_baton,apr_hash_t * parameters,const char * realmstring,apr_pool_t * pool)436 simple_gnome_keyring_first_creds(void **credentials,
437 void **iter_baton,
438 void *provider_baton,
439 apr_hash_t *parameters,
440 const char *realmstring,
441 apr_pool_t *pool)
442 {
443 return svn_auth__simple_creds_cache_get(credentials,
444 iter_baton, provider_baton,
445 parameters, realmstring,
446 password_get_gnome_keyring,
447 SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE,
448 pool);
449 }
450
451 /* Save encrypted credentials to the simple provider's cache. */
452 static svn_error_t *
simple_gnome_keyring_save_creds(svn_boolean_t * saved,void * credentials,void * provider_baton,apr_hash_t * parameters,const char * realmstring,apr_pool_t * pool)453 simple_gnome_keyring_save_creds(svn_boolean_t *saved,
454 void *credentials,
455 void *provider_baton,
456 apr_hash_t *parameters,
457 const char *realmstring,
458 apr_pool_t *pool)
459 {
460 return svn_auth__simple_creds_cache_set(saved, credentials,
461 provider_baton, parameters,
462 realmstring,
463 password_set_gnome_keyring,
464 SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE,
465 pool);
466 }
467
468 static const svn_auth_provider_t gnome_keyring_simple_provider = {
469 SVN_AUTH_CRED_SIMPLE,
470 simple_gnome_keyring_first_creds,
471 NULL,
472 simple_gnome_keyring_save_creds
473 };
474
475 /* Public API */
476 void
svn_auth_get_gnome_keyring_simple_provider(svn_auth_provider_object_t ** provider,apr_pool_t * pool)477 svn_auth_get_gnome_keyring_simple_provider
478 (svn_auth_provider_object_t **provider,
479 apr_pool_t *pool)
480 {
481 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
482
483 po->vtable = &gnome_keyring_simple_provider;
484 *provider = po;
485
486 #ifdef SVN_HAVE_GNOME_KEYRING
487 init_gnome_keyring();
488 #endif
489 }
490
491
492
493 /*-----------------------------------------------------------------------*/
494 /* GNOME Keyring SSL client certificate passphrase provider, */
495 /* puts passphrases in GNOME Keyring */
496 /*-----------------------------------------------------------------------*/
497
498 /* Get cached encrypted credentials from the ssl client cert password
499 provider's cache. */
500 static svn_error_t *
ssl_client_cert_pw_gnome_keyring_first_creds(void ** credentials,void ** iter_baton,void * provider_baton,apr_hash_t * parameters,const char * realmstring,apr_pool_t * pool)501 ssl_client_cert_pw_gnome_keyring_first_creds(void **credentials,
502 void **iter_baton,
503 void *provider_baton,
504 apr_hash_t *parameters,
505 const char *realmstring,
506 apr_pool_t *pool)
507 {
508 return svn_auth__ssl_client_cert_pw_cache_get(
509 credentials, iter_baton, provider_baton, parameters, realmstring,
510 password_get_gnome_keyring, SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE,
511 pool);
512 }
513
514 /* Save encrypted credentials to the ssl client cert password provider's
515 cache. */
516 static svn_error_t *
ssl_client_cert_pw_gnome_keyring_save_creds(svn_boolean_t * saved,void * credentials,void * provider_baton,apr_hash_t * parameters,const char * realmstring,apr_pool_t * pool)517 ssl_client_cert_pw_gnome_keyring_save_creds(svn_boolean_t *saved,
518 void *credentials,
519 void *provider_baton,
520 apr_hash_t *parameters,
521 const char *realmstring,
522 apr_pool_t *pool)
523 {
524 return svn_auth__ssl_client_cert_pw_cache_set(
525 saved, credentials, provider_baton, parameters, realmstring,
526 password_set_gnome_keyring, SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE,
527 pool);
528 }
529
530 static const svn_auth_provider_t gnome_keyring_ssl_client_cert_pw_provider = {
531 SVN_AUTH_CRED_SSL_CLIENT_CERT_PW,
532 ssl_client_cert_pw_gnome_keyring_first_creds,
533 NULL,
534 ssl_client_cert_pw_gnome_keyring_save_creds
535 };
536
537 /* Public API */
538 void
svn_auth_get_gnome_keyring_ssl_client_cert_pw_provider(svn_auth_provider_object_t ** provider,apr_pool_t * pool)539 svn_auth_get_gnome_keyring_ssl_client_cert_pw_provider
540 (svn_auth_provider_object_t **provider,
541 apr_pool_t *pool)
542 {
543 svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
544
545 po->vtable = &gnome_keyring_ssl_client_cert_pw_provider;
546 *provider = po;
547
548 #ifdef SVN_HAVE_GNOME_KEYRING
549 init_gnome_keyring();
550 #endif
551 }
552