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
28/*** Includes. ***/
29
30#include <apr_pools.h>
31#include <apr_strings.h>
32#include <glib.h>
33#include <gnome-keyring.h>
34
35#include "svn_auth.h"
36#include "svn_config.h"
37#include "svn_error.h"
38#include "svn_hash.h"
39#include "svn_pools.h"
40
41#include "private/svn_auth_private.h"
42
43#include "svn_private_config.h"
44
45
46
47/*-----------------------------------------------------------------------*/
48/* GNOME Keyring simple provider, puts passwords in GNOME Keyring        */
49/*-----------------------------------------------------------------------*/
50
51
52struct gnome_keyring_baton
53{
54  const char *keyring_name;
55  GnomeKeyringInfo *info;
56  GMainLoop *loop;
57};
58
59
60/* Callback function to destroy gnome_keyring_baton. */
61static void
62callback_destroy_data_keyring(void *data)
63{
64  struct gnome_keyring_baton *key_info = data;
65
66  if (data == NULL)
67    return;
68
69  free((void*)key_info->keyring_name);
70  key_info->keyring_name = NULL;
71
72  if (key_info->info)
73    {
74      gnome_keyring_info_free(key_info->info);
75      key_info->info = NULL;
76    }
77
78  return;
79}
80
81
82/* Callback function to complete the keyring operation. */
83static void
84callback_done(GnomeKeyringResult result,
85              gpointer data)
86{
87  struct gnome_keyring_baton *key_info = data;
88
89  g_main_loop_quit(key_info->loop);
90  return;
91}
92
93
94/* Callback function to get the keyring info. */
95static void
96callback_get_info_keyring(GnomeKeyringResult result,
97                          GnomeKeyringInfo *info,
98                          void *data)
99{
100  struct gnome_keyring_baton *key_info = data;
101
102  if (result == GNOME_KEYRING_RESULT_OK && info != NULL)
103    {
104      key_info->info = gnome_keyring_info_copy(info);
105    }
106  else
107    {
108      if (key_info->info != NULL)
109        gnome_keyring_info_free(key_info->info);
110
111      key_info->info = NULL;
112    }
113
114  g_main_loop_quit(key_info->loop);
115
116  return;
117}
118
119
120/* Callback function to get the default keyring string name. */
121static void
122callback_default_keyring(GnomeKeyringResult result,
123                         const char *string,
124                         void *data)
125{
126  struct gnome_keyring_baton *key_info = data;
127
128  if (result == GNOME_KEYRING_RESULT_OK && string != NULL)
129    {
130      key_info->keyring_name = strdup(string);
131    }
132  else
133    {
134      free((void*)key_info->keyring_name);
135      key_info->keyring_name = NULL;
136    }
137
138  g_main_loop_quit(key_info->loop);
139
140  return;
141}
142
143/* Returns the default keyring name, allocated in RESULT_POOL. */
144static char*
145get_default_keyring_name(apr_pool_t *result_pool)
146{
147  char *def = NULL;
148  struct gnome_keyring_baton key_info;
149
150  key_info.info = NULL;
151  key_info.keyring_name = NULL;
152
153  /* Finds default keyring. */
154  key_info.loop = g_main_loop_new(NULL, FALSE);
155  gnome_keyring_get_default_keyring(callback_default_keyring, &key_info, NULL);
156  g_main_loop_run(key_info.loop);
157
158  if (key_info.keyring_name == NULL)
159    {
160      callback_destroy_data_keyring(&key_info);
161      return NULL;
162    }
163
164  def = apr_pstrdup(result_pool, key_info.keyring_name);
165  callback_destroy_data_keyring(&key_info);
166
167  return def;
168}
169
170/* Returns TRUE if the KEYRING_NAME is locked. */
171static svn_boolean_t
172check_keyring_is_locked(const char *keyring_name)
173{
174  struct gnome_keyring_baton key_info;
175
176  key_info.info = NULL;
177  key_info.keyring_name = NULL;
178
179  /* Get details about the default keyring. */
180  key_info.loop = g_main_loop_new(NULL, FALSE);
181  gnome_keyring_get_info(keyring_name, callback_get_info_keyring, &key_info,
182                         NULL);
183  g_main_loop_run(key_info.loop);
184
185  if (key_info.info == NULL)
186    {
187      callback_destroy_data_keyring(&key_info);
188      return FALSE;
189    }
190
191  /* Check if keyring is locked. */
192  if (gnome_keyring_info_get_is_locked(key_info.info))
193    return TRUE;
194  else
195    return FALSE;
196}
197
198/* Unlock the KEYRING_NAME with the KEYRING_PASSWORD. If KEYRING was
199   successfully unlocked return TRUE. */
200static svn_boolean_t
201unlock_gnome_keyring(const char *keyring_name,
202                     const char *keyring_password,
203                     apr_pool_t *pool)
204{
205  struct gnome_keyring_baton key_info;
206
207  key_info.info = NULL;
208  key_info.keyring_name = NULL;
209
210  /* Get details about the default keyring. */
211  key_info.loop = g_main_loop_new(NULL, FALSE);
212  gnome_keyring_get_info(keyring_name, callback_get_info_keyring,
213                         &key_info, NULL);
214  g_main_loop_run(key_info.loop);
215
216  if (key_info.info == NULL)
217    {
218      callback_destroy_data_keyring(&key_info);
219      return FALSE;
220    }
221  else
222    {
223      key_info.loop = g_main_loop_new(NULL, FALSE);
224      gnome_keyring_unlock(keyring_name, keyring_password,
225                           callback_done, &key_info, NULL);
226      g_main_loop_run(key_info.loop);
227    }
228  callback_destroy_data_keyring(&key_info);
229  if (check_keyring_is_locked(keyring_name))
230    return FALSE;
231
232  return TRUE;
233}
234
235
236/* There is a race here: this ensures keyring is unlocked just now,
237   but will it still be unlocked when we use it? */
238static svn_error_t *
239ensure_gnome_keyring_is_unlocked(svn_boolean_t non_interactive,
240                                 apr_hash_t *parameters,
241                                 apr_pool_t *scratch_pool)
242{
243  const char *default_keyring = get_default_keyring_name(scratch_pool);
244
245  if (! non_interactive)
246    {
247      svn_auth_gnome_keyring_unlock_prompt_func_t unlock_prompt_func =
248        svn_hash_gets(parameters,
249                      SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_FUNC);
250      void *unlock_prompt_baton =
251        svn_hash_gets(parameters,
252                      SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_BATON);
253
254      char *keyring_password;
255
256      if (unlock_prompt_func && check_keyring_is_locked(default_keyring))
257        {
258          SVN_ERR((*unlock_prompt_func)(&keyring_password,
259                                        default_keyring,
260                                        unlock_prompt_baton,
261                                        scratch_pool));
262
263          /* If keyring is locked give up and try the next provider. */
264          if (! unlock_gnome_keyring(default_keyring, keyring_password,
265                                     scratch_pool))
266            return SVN_NO_ERROR;
267        }
268    }
269  else
270    {
271      if (check_keyring_is_locked(default_keyring))
272        {
273          return svn_error_create(SVN_ERR_AUTHN_CREDS_UNAVAILABLE, NULL,
274                                  _("GNOME Keyring is locked and "
275                                    "we are non-interactive"));
276        }
277    }
278
279  return SVN_NO_ERROR;
280}
281
282/* Implementation of svn_auth__password_get_t that retrieves the password
283   from GNOME Keyring. */
284static svn_error_t *
285password_get_gnome_keyring(svn_boolean_t *done,
286                           const char **password,
287                           apr_hash_t *creds,
288                           const char *realmstring,
289                           const char *username,
290                           apr_hash_t *parameters,
291                           svn_boolean_t non_interactive,
292                           apr_pool_t *pool)
293{
294  GnomeKeyringResult result;
295  GList *items;
296
297  *done = FALSE;
298
299  SVN_ERR(ensure_gnome_keyring_is_unlocked(non_interactive, parameters, pool));
300
301  if (! svn_hash_gets(parameters, "gnome-keyring-opening-failed"))
302    {
303      result = gnome_keyring_find_network_password_sync(username, realmstring,
304                                                        NULL, NULL, NULL, NULL,
305                                                        0, &items);
306    }
307  else
308    {
309      result = GNOME_KEYRING_RESULT_DENIED;
310    }
311
312  if (result == GNOME_KEYRING_RESULT_OK)
313    {
314      if (items && items->data)
315        {
316          GnomeKeyringNetworkPasswordData *item = items->data;
317          if (item->password)
318            {
319              size_t len = strlen(item->password);
320              if (len > 0)
321                {
322                  *password = apr_pstrmemdup(pool, item->password, len);
323                  *done = TRUE;
324                }
325            }
326          gnome_keyring_network_password_list_free(items);
327        }
328    }
329  else
330    {
331      svn_hash_sets(parameters, "gnome-keyring-opening-failed", "");
332    }
333
334  return SVN_NO_ERROR;
335}
336
337/* Implementation of svn_auth__password_set_t that stores the password in
338   GNOME Keyring. */
339static svn_error_t *
340password_set_gnome_keyring(svn_boolean_t *done,
341                           apr_hash_t *creds,
342                           const char *realmstring,
343                           const char *username,
344                           const char *password,
345                           apr_hash_t *parameters,
346                           svn_boolean_t non_interactive,
347                           apr_pool_t *pool)
348{
349  GnomeKeyringResult result;
350  guint32 item_id;
351
352  *done = FALSE;
353
354  SVN_ERR(ensure_gnome_keyring_is_unlocked(non_interactive, parameters, pool));
355
356  if (! svn_hash_gets(parameters, "gnome-keyring-opening-failed"))
357    {
358      result = gnome_keyring_set_network_password_sync(NULL, /* default keyring */
359                                                       username, realmstring,
360                                                       NULL, NULL, NULL, NULL,
361                                                       0, password,
362                                                       &item_id);
363    }
364  else
365    {
366      result = GNOME_KEYRING_RESULT_DENIED;
367    }
368  if (result != GNOME_KEYRING_RESULT_OK)
369    {
370      svn_hash_sets(parameters, "gnome-keyring-opening-failed", "");
371    }
372
373  *done = (result == GNOME_KEYRING_RESULT_OK);
374  return SVN_NO_ERROR;
375}
376
377/* Get cached encrypted credentials from the simple provider's cache. */
378static svn_error_t *
379simple_gnome_keyring_first_creds(void **credentials,
380                                 void **iter_baton,
381                                 void *provider_baton,
382                                 apr_hash_t *parameters,
383                                 const char *realmstring,
384                                 apr_pool_t *pool)
385{
386  return svn_auth__simple_creds_cache_get(credentials,
387                                          iter_baton, provider_baton,
388                                          parameters, realmstring,
389                                          password_get_gnome_keyring,
390                                          SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE,
391                                          pool);
392}
393
394/* Save encrypted credentials to the simple provider's cache. */
395static svn_error_t *
396simple_gnome_keyring_save_creds(svn_boolean_t *saved,
397                                void *credentials,
398                                void *provider_baton,
399                                apr_hash_t *parameters,
400                                const char *realmstring,
401                                apr_pool_t *pool)
402{
403  return svn_auth__simple_creds_cache_set(saved, credentials,
404                                          provider_baton, parameters,
405                                          realmstring,
406                                          password_set_gnome_keyring,
407                                          SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE,
408                                          pool);
409}
410
411#if GLIB_CHECK_VERSION(2,6,0)
412static void
413log_noop(const gchar *log_domain, GLogLevelFlags log_level,
414         const gchar *message, gpointer user_data)
415{
416  /* do nothing */
417}
418#endif
419
420static void
421init_gnome_keyring(void)
422{
423  const char *application_name = NULL;
424  application_name = g_get_application_name();
425  if (!application_name)
426    g_set_application_name("Subversion");
427
428  /* Ideally we call g_log_set_handler() with a log_domain specific to
429     libgnome-keyring.  Unfortunately, at least as of gnome-keyring
430     2.22.3, it doesn't have its own log_domain.  As a result, we
431     suppress stderr spam for not only libgnome-keyring, but for
432     anything else the app is linked to that uses glib logging and
433     doesn't specify a log_domain. */
434#if GLIB_CHECK_VERSION(2,6,0)
435  g_log_set_default_handler(log_noop, NULL);
436#endif
437}
438
439static const svn_auth_provider_t gnome_keyring_simple_provider = {
440  SVN_AUTH_CRED_SIMPLE,
441  simple_gnome_keyring_first_creds,
442  NULL,
443  simple_gnome_keyring_save_creds
444};
445
446/* Public API */
447void
448svn_auth_get_gnome_keyring_simple_provider
449    (svn_auth_provider_object_t **provider,
450     apr_pool_t *pool)
451{
452  svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
453
454  po->vtable = &gnome_keyring_simple_provider;
455  *provider = po;
456
457  init_gnome_keyring();
458}
459
460
461/*-----------------------------------------------------------------------*/
462/* GNOME Keyring SSL client certificate passphrase provider,             */
463/* puts passphrases in GNOME Keyring                                     */
464/*-----------------------------------------------------------------------*/
465
466/* Get cached encrypted credentials from the ssl client cert password
467   provider's cache. */
468static svn_error_t *
469ssl_client_cert_pw_gnome_keyring_first_creds(void **credentials,
470                                             void **iter_baton,
471                                             void *provider_baton,
472                                             apr_hash_t *parameters,
473                                             const char *realmstring,
474                                             apr_pool_t *pool)
475{
476  return svn_auth__ssl_client_cert_pw_cache_get(
477             credentials, iter_baton, provider_baton, parameters, realmstring,
478             password_get_gnome_keyring, SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE,
479             pool);
480}
481
482/* Save encrypted credentials to the ssl client cert password provider's
483   cache. */
484static svn_error_t *
485ssl_client_cert_pw_gnome_keyring_save_creds(svn_boolean_t *saved,
486                                            void *credentials,
487                                            void *provider_baton,
488                                            apr_hash_t *parameters,
489                                            const char *realmstring,
490                                            apr_pool_t *pool)
491{
492  return svn_auth__ssl_client_cert_pw_cache_set(
493             saved, credentials, provider_baton, parameters, realmstring,
494             password_set_gnome_keyring, SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE,
495             pool);
496}
497
498static const svn_auth_provider_t gnome_keyring_ssl_client_cert_pw_provider = {
499  SVN_AUTH_CRED_SSL_CLIENT_CERT_PW,
500  ssl_client_cert_pw_gnome_keyring_first_creds,
501  NULL,
502  ssl_client_cert_pw_gnome_keyring_save_creds
503};
504
505/* Public API */
506void
507svn_auth_get_gnome_keyring_ssl_client_cert_pw_provider
508    (svn_auth_provider_object_t **provider,
509     apr_pool_t *pool)
510{
511  svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
512
513  po->vtable = &gnome_keyring_ssl_client_cert_pw_provider;
514  *provider = po;
515
516  init_gnome_keyring();
517}
518