xref: /freebsd-11-stable/contrib/subversion/subversion/libsvn_auth_gnome_keyring/gnome_keyring.c (revision 3c9339f7792540596bf97077a8f403e944af7f39)
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