]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/libsvn_auth_gnome_keyring/gnome_keyring.c
Update to Zstandard 1.4.4
[FreeBSD/FreeBSD.git] / contrib / subversion / subversion / libsvn_auth_gnome_keyring / gnome_keyring.c
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 \f
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
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 *
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   if (!available_collection(non_interactive, pool))
123     return SVN_NO_ERROR;
124   
125   gpassword = secret_password_lookup_sync(SECRET_SCHEMA_COMPAT_NETWORK, NULL,
126                                           &gerror,
127                                           "domain", realmstring,
128                                           "user", username,
129                                           NULL);
130   if (gerror)
131     {
132       g_error_free(gerror);
133     }
134   else if (gpassword)
135     {
136       *password = apr_pstrdup(pool, gpassword);
137       g_free(gpassword);
138       *done = TRUE;
139     }
140   
141   return SVN_NO_ERROR;
142 }
143
144 /* Implementation of svn_auth__password_set_t that stores the password
145    using libsecret. */
146 static svn_error_t *
147 password_set_gnome_keyring(svn_boolean_t *done,
148                            apr_hash_t *creds,
149                            const char *realmstring,
150                            const char *username,
151                            const char *password,
152                            apr_hash_t *parameters,
153                            svn_boolean_t non_interactive,
154                            apr_pool_t *pool)
155 {
156   GError *gerror = NULL;
157   gboolean gstatus;
158   
159   if (!available_collection(non_interactive, pool))
160     return SVN_NO_ERROR;
161
162   /* "network password" is the label used by the old libgnome-keyring. */
163   gstatus = secret_password_store_sync(SECRET_SCHEMA_COMPAT_NETWORK,
164                                        SECRET_COLLECTION_DEFAULT,
165                                        "network password",
166                                        password,
167                                        NULL, &gerror,
168                                        "domain", realmstring,
169                                        "user", username,
170                                        NULL);
171   if (gerror)
172     {
173       g_error_free(gerror);
174     }
175   else if (gstatus)
176     {
177       *done = TRUE;
178     }
179   
180   return SVN_NO_ERROR;
181 }
182
183 #endif /* SVN_HAVE_LIBSECRET */
184
185 #ifdef SVN_HAVE_GNOME_KEYRING
186
187 #include <glib.h>
188 #include <gnome-keyring.h>
189
190 /* Returns the default keyring name, allocated in RESULT_POOL. */
191 static char*
192 get_default_keyring_name(apr_pool_t *result_pool)
193 {
194   char *name, *def;
195   GnomeKeyringResult gkr;
196
197   gkr = gnome_keyring_get_default_keyring_sync(&name);
198   if (gkr != GNOME_KEYRING_RESULT_OK)
199     return NULL;
200
201   def = apr_pstrdup(result_pool, name);
202   g_free(name);
203
204   return def;
205 }
206
207 /* Returns TRUE if the KEYRING_NAME is locked. */
208 static svn_boolean_t
209 check_keyring_is_locked(const char *keyring_name)
210 {
211   GnomeKeyringInfo *info;
212   svn_boolean_t locked;
213   GnomeKeyringResult gkr;
214
215   gkr = gnome_keyring_get_info_sync(keyring_name, &info);
216   if (gkr != GNOME_KEYRING_RESULT_OK)
217     return FALSE;
218
219   if (gnome_keyring_info_get_is_locked(info))
220     locked = TRUE;
221   else
222     locked = FALSE;
223
224   gnome_keyring_info_free(info);
225
226   return locked;
227 }
228
229 /* Unlock the KEYRING_NAME with the KEYRING_PASSWORD. If KEYRING was
230    successfully unlocked return TRUE. */
231 static svn_boolean_t
232 unlock_gnome_keyring(const char *keyring_name,
233                      const char *keyring_password,
234                      apr_pool_t *pool)
235 {
236   GnomeKeyringInfo *info;
237   GnomeKeyringResult gkr;
238
239   gkr = gnome_keyring_get_info_sync(keyring_name, &info);
240   if (gkr != GNOME_KEYRING_RESULT_OK)
241     return FALSE;
242
243   gkr = gnome_keyring_unlock_sync(keyring_name, keyring_password);
244   gnome_keyring_info_free(info);
245   if (gkr != GNOME_KEYRING_RESULT_OK)
246     return FALSE;
247
248   return check_keyring_is_locked(keyring_name);
249 }
250
251
252 /* There is a race here: this ensures keyring is unlocked just now,
253    but will it still be unlocked when we use it? */
254 static svn_error_t *
255 ensure_gnome_keyring_is_unlocked(svn_boolean_t non_interactive,
256                                  apr_hash_t *parameters,
257                                  apr_pool_t *scratch_pool)
258 {
259   const char *default_keyring = get_default_keyring_name(scratch_pool);
260
261   if (! non_interactive)
262     {
263       svn_auth_gnome_keyring_unlock_prompt_func_t unlock_prompt_func =
264         svn_hash_gets(parameters,
265                       SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_FUNC);
266       void *unlock_prompt_baton =
267         svn_hash_gets(parameters,
268                       SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_BATON);
269
270       char *keyring_password;
271
272       if (unlock_prompt_func && check_keyring_is_locked(default_keyring))
273         {
274           SVN_ERR((*unlock_prompt_func)(&keyring_password,
275                                         default_keyring,
276                                         unlock_prompt_baton,
277                                         scratch_pool));
278
279           /* If keyring is locked give up and try the next provider. */
280           if (! unlock_gnome_keyring(default_keyring, keyring_password,
281                                      scratch_pool))
282             return SVN_NO_ERROR;
283         }
284     }
285   else
286     {
287       if (check_keyring_is_locked(default_keyring))
288         {
289           return svn_error_create(SVN_ERR_AUTHN_CREDS_UNAVAILABLE, NULL,
290                                   _("GNOME Keyring is locked and "
291                                     "we are non-interactive"));
292         }
293     }
294
295   return SVN_NO_ERROR;
296 }
297
298 /* Implementation of svn_auth__password_get_t that retrieves the password
299    from GNOME Keyring. */
300 static svn_error_t *
301 password_get_gnome_keyring(svn_boolean_t *done,
302                            const char **password,
303                            apr_hash_t *creds,
304                            const char *realmstring,
305                            const char *username,
306                            apr_hash_t *parameters,
307                            svn_boolean_t non_interactive,
308                            apr_pool_t *pool)
309 {
310   GnomeKeyringResult result;
311   GList *items;
312
313   *done = FALSE;
314
315   SVN_ERR(ensure_gnome_keyring_is_unlocked(non_interactive, parameters, pool));
316
317   if (! svn_hash_gets(parameters, "gnome-keyring-opening-failed"))
318     {
319       result = gnome_keyring_find_network_password_sync(username, realmstring,
320                                                         NULL, NULL, NULL, NULL,
321                                                         0, &items);
322     }
323   else
324     {
325       result = GNOME_KEYRING_RESULT_DENIED;
326     }
327
328   if (result == GNOME_KEYRING_RESULT_OK)
329     {
330       if (items && items->data)
331         {
332           GnomeKeyringNetworkPasswordData *item = items->data;
333           if (item->password)
334             {
335               size_t len = strlen(item->password);
336               if (len > 0)
337                 {
338                   *password = apr_pstrmemdup(pool, item->password, len);
339                   *done = TRUE;
340                 }
341             }
342           gnome_keyring_network_password_list_free(items);
343         }
344     }
345   else
346     {
347       svn_hash_sets(parameters, "gnome-keyring-opening-failed", "");
348     }
349
350   return SVN_NO_ERROR;
351 }
352
353 /* Implementation of svn_auth__password_set_t that stores the password in
354    GNOME Keyring. */
355 static svn_error_t *
356 password_set_gnome_keyring(svn_boolean_t *done,
357                            apr_hash_t *creds,
358                            const char *realmstring,
359                            const char *username,
360                            const char *password,
361                            apr_hash_t *parameters,
362                            svn_boolean_t non_interactive,
363                            apr_pool_t *pool)
364 {
365   GnomeKeyringResult result;
366   guint32 item_id;
367
368   *done = FALSE;
369
370   SVN_ERR(ensure_gnome_keyring_is_unlocked(non_interactive, parameters, pool));
371
372   if (! svn_hash_gets(parameters, "gnome-keyring-opening-failed"))
373     {
374       result = gnome_keyring_set_network_password_sync(NULL, /* default keyring */
375                                                        username, realmstring,
376                                                        NULL, NULL, NULL, NULL,
377                                                        0, password,
378                                                        &item_id);
379     }
380   else
381     {
382       result = GNOME_KEYRING_RESULT_DENIED;
383     }
384   if (result != GNOME_KEYRING_RESULT_OK)
385     {
386       svn_hash_sets(parameters, "gnome-keyring-opening-failed", "");
387     }
388
389   *done = (result == GNOME_KEYRING_RESULT_OK);
390   return SVN_NO_ERROR;
391 }
392
393 #if GLIB_CHECK_VERSION(2,6,0)
394 static void
395 log_noop(const gchar *log_domain, GLogLevelFlags log_level,
396          const gchar *message, gpointer user_data)
397 {
398   /* do nothing */
399 }
400 #endif
401
402 static void
403 init_gnome_keyring(void)
404 {
405   const char *application_name = NULL;
406   application_name = g_get_application_name();
407   if (!application_name)
408     g_set_application_name("Subversion");
409
410   /* Ideally we call g_log_set_handler() with a log_domain specific to
411      libgnome-keyring.  Unfortunately, at least as of gnome-keyring
412      2.22.3, it doesn't have its own log_domain.  As a result, we
413      suppress stderr spam for not only libgnome-keyring, but for
414      anything else the app is linked to that uses glib logging and
415      doesn't specify a log_domain. */
416 #if GLIB_CHECK_VERSION(2,6,0)
417   g_log_set_default_handler(log_noop, NULL);
418 #endif
419 }
420
421 #endif /* SVN_HAVE_GNOME_KEYRING */
422
423 \f
424 /*-----------------------------------------------------------------------*/
425 /* GNOME Keyring simple provider, puts passwords in GNOME Keyring        */
426 /*-----------------------------------------------------------------------*/
427
428 /* Get cached encrypted credentials from the simple provider's cache. */
429 static svn_error_t *
430 simple_gnome_keyring_first_creds(void **credentials,
431                                  void **iter_baton,
432                                  void *provider_baton,
433                                  apr_hash_t *parameters,
434                                  const char *realmstring,
435                                  apr_pool_t *pool)
436 {
437   return svn_auth__simple_creds_cache_get(credentials,
438                                           iter_baton, provider_baton,
439                                           parameters, realmstring,
440                                           password_get_gnome_keyring,
441                                           SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE,
442                                           pool);
443 }
444
445 /* Save encrypted credentials to the simple provider's cache. */
446 static svn_error_t *
447 simple_gnome_keyring_save_creds(svn_boolean_t *saved,
448                                 void *credentials,
449                                 void *provider_baton,
450                                 apr_hash_t *parameters,
451                                 const char *realmstring,
452                                 apr_pool_t *pool)
453 {
454   return svn_auth__simple_creds_cache_set(saved, credentials,
455                                           provider_baton, parameters,
456                                           realmstring,
457                                           password_set_gnome_keyring,
458                                           SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE,
459                                           pool);
460 }
461
462 static const svn_auth_provider_t gnome_keyring_simple_provider = {
463   SVN_AUTH_CRED_SIMPLE,
464   simple_gnome_keyring_first_creds,
465   NULL,
466   simple_gnome_keyring_save_creds
467 };
468
469 /* Public API */
470 void
471 svn_auth_get_gnome_keyring_simple_provider
472     (svn_auth_provider_object_t **provider,
473      apr_pool_t *pool)
474 {
475   svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
476
477   po->vtable = &gnome_keyring_simple_provider;
478   *provider = po;
479
480 #ifdef SVN_HAVE_GNOME_KEYRING
481   init_gnome_keyring();
482 #endif
483 }
484
485
486 \f
487 /*-----------------------------------------------------------------------*/
488 /* GNOME Keyring SSL client certificate passphrase provider,             */
489 /* puts passphrases in GNOME Keyring                                     */
490 /*-----------------------------------------------------------------------*/
491
492 /* Get cached encrypted credentials from the ssl client cert password
493    provider's cache. */
494 static svn_error_t *
495 ssl_client_cert_pw_gnome_keyring_first_creds(void **credentials,
496                                              void **iter_baton,
497                                              void *provider_baton,
498                                              apr_hash_t *parameters,
499                                              const char *realmstring,
500                                              apr_pool_t *pool)
501 {
502   return svn_auth__ssl_client_cert_pw_cache_get(
503              credentials, iter_baton, provider_baton, parameters, realmstring,
504              password_get_gnome_keyring, SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE,
505              pool);
506 }
507
508 /* Save encrypted credentials to the ssl client cert password provider's
509    cache. */
510 static svn_error_t *
511 ssl_client_cert_pw_gnome_keyring_save_creds(svn_boolean_t *saved,
512                                             void *credentials,
513                                             void *provider_baton,
514                                             apr_hash_t *parameters,
515                                             const char *realmstring,
516                                             apr_pool_t *pool)
517 {
518   return svn_auth__ssl_client_cert_pw_cache_set(
519              saved, credentials, provider_baton, parameters, realmstring,
520              password_set_gnome_keyring, SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE,
521              pool);
522 }
523
524 static const svn_auth_provider_t gnome_keyring_ssl_client_cert_pw_provider = {
525   SVN_AUTH_CRED_SSL_CLIENT_CERT_PW,
526   ssl_client_cert_pw_gnome_keyring_first_creds,
527   NULL,
528   ssl_client_cert_pw_gnome_keyring_save_creds
529 };
530
531 /* Public API */
532 void
533 svn_auth_get_gnome_keyring_ssl_client_cert_pw_provider
534     (svn_auth_provider_object_t **provider,
535      apr_pool_t *pool)
536 {
537   svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
538
539   po->vtable = &gnome_keyring_ssl_client_cert_pw_provider;
540   *provider = po;
541
542 #ifdef SVN_HAVE_GNOME_KEYRING
543   init_gnome_keyring();
544 #endif
545 }