gnome_keyring.c revision 289166
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
52/* Returns the default keyring name, allocated in RESULT_POOL. */
53static char*
54get_default_keyring_name(apr_pool_t *result_pool)
55{
56  char *name, *def;
57  GnomeKeyringResult gkr;
58
59  gkr = gnome_keyring_get_default_keyring_sync(&name);
60  if (gkr != GNOME_KEYRING_RESULT_OK)
61    return NULL;
62
63  def = apr_pstrdup(result_pool, name);
64  g_free(name);
65
66  return def;
67}
68
69/* Returns TRUE if the KEYRING_NAME is locked. */
70static svn_boolean_t
71check_keyring_is_locked(const char *keyring_name)
72{
73  GnomeKeyringInfo *info;
74  svn_boolean_t locked;
75  GnomeKeyringResult gkr;
76
77  gkr = gnome_keyring_get_info_sync(keyring_name, &info);
78  if (gkr != GNOME_KEYRING_RESULT_OK)
79    return FALSE;
80
81  if (gnome_keyring_info_get_is_locked(info))
82    locked = TRUE;
83  else
84    locked = FALSE;
85
86  gnome_keyring_info_free(info);
87
88  return locked;
89}
90
91/* Unlock the KEYRING_NAME with the KEYRING_PASSWORD. If KEYRING was
92   successfully unlocked return TRUE. */
93static svn_boolean_t
94unlock_gnome_keyring(const char *keyring_name,
95                     const char *keyring_password,
96                     apr_pool_t *pool)
97{
98  GnomeKeyringInfo *info;
99  GnomeKeyringResult gkr;
100
101  gkr = gnome_keyring_get_info_sync(keyring_name, &info);
102  if (gkr != GNOME_KEYRING_RESULT_OK)
103    return FALSE;
104
105  gkr = gnome_keyring_unlock_sync(keyring_name, keyring_password);
106  gnome_keyring_info_free(info);
107  if (gkr != GNOME_KEYRING_RESULT_OK)
108    return FALSE;
109
110  return check_keyring_is_locked(keyring_name);
111}
112
113
114/* There is a race here: this ensures keyring is unlocked just now,
115   but will it still be unlocked when we use it? */
116static svn_error_t *
117ensure_gnome_keyring_is_unlocked(svn_boolean_t non_interactive,
118                                 apr_hash_t *parameters,
119                                 apr_pool_t *scratch_pool)
120{
121  const char *default_keyring = get_default_keyring_name(scratch_pool);
122
123  if (! non_interactive)
124    {
125      svn_auth_gnome_keyring_unlock_prompt_func_t unlock_prompt_func =
126        svn_hash_gets(parameters,
127                      SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_FUNC);
128      void *unlock_prompt_baton =
129        svn_hash_gets(parameters,
130                      SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_BATON);
131
132      char *keyring_password;
133
134      if (unlock_prompt_func && check_keyring_is_locked(default_keyring))
135        {
136          SVN_ERR((*unlock_prompt_func)(&keyring_password,
137                                        default_keyring,
138                                        unlock_prompt_baton,
139                                        scratch_pool));
140
141          /* If keyring is locked give up and try the next provider. */
142          if (! unlock_gnome_keyring(default_keyring, keyring_password,
143                                     scratch_pool))
144            return SVN_NO_ERROR;
145        }
146    }
147  else
148    {
149      if (check_keyring_is_locked(default_keyring))
150        {
151          return svn_error_create(SVN_ERR_AUTHN_CREDS_UNAVAILABLE, NULL,
152                                  _("GNOME Keyring is locked and "
153                                    "we are non-interactive"));
154        }
155    }
156
157  return SVN_NO_ERROR;
158}
159
160/* Implementation of svn_auth__password_get_t that retrieves the password
161   from GNOME Keyring. */
162static svn_error_t *
163password_get_gnome_keyring(svn_boolean_t *done,
164                           const char **password,
165                           apr_hash_t *creds,
166                           const char *realmstring,
167                           const char *username,
168                           apr_hash_t *parameters,
169                           svn_boolean_t non_interactive,
170                           apr_pool_t *pool)
171{
172  GnomeKeyringResult result;
173  GList *items;
174
175  *done = FALSE;
176
177  SVN_ERR(ensure_gnome_keyring_is_unlocked(non_interactive, parameters, pool));
178
179  if (! svn_hash_gets(parameters, "gnome-keyring-opening-failed"))
180    {
181      result = gnome_keyring_find_network_password_sync(username, realmstring,
182                                                        NULL, NULL, NULL, NULL,
183                                                        0, &items);
184    }
185  else
186    {
187      result = GNOME_KEYRING_RESULT_DENIED;
188    }
189
190  if (result == GNOME_KEYRING_RESULT_OK)
191    {
192      if (items && items->data)
193        {
194          GnomeKeyringNetworkPasswordData *item = items->data;
195          if (item->password)
196            {
197              size_t len = strlen(item->password);
198              if (len > 0)
199                {
200                  *password = apr_pstrmemdup(pool, item->password, len);
201                  *done = TRUE;
202                }
203            }
204          gnome_keyring_network_password_list_free(items);
205        }
206    }
207  else
208    {
209      svn_hash_sets(parameters, "gnome-keyring-opening-failed", "");
210    }
211
212  return SVN_NO_ERROR;
213}
214
215/* Implementation of svn_auth__password_set_t that stores the password in
216   GNOME Keyring. */
217static svn_error_t *
218password_set_gnome_keyring(svn_boolean_t *done,
219                           apr_hash_t *creds,
220                           const char *realmstring,
221                           const char *username,
222                           const char *password,
223                           apr_hash_t *parameters,
224                           svn_boolean_t non_interactive,
225                           apr_pool_t *pool)
226{
227  GnomeKeyringResult result;
228  guint32 item_id;
229
230  *done = FALSE;
231
232  SVN_ERR(ensure_gnome_keyring_is_unlocked(non_interactive, parameters, pool));
233
234  if (! svn_hash_gets(parameters, "gnome-keyring-opening-failed"))
235    {
236      result = gnome_keyring_set_network_password_sync(NULL, /* default keyring */
237                                                       username, realmstring,
238                                                       NULL, NULL, NULL, NULL,
239                                                       0, password,
240                                                       &item_id);
241    }
242  else
243    {
244      result = GNOME_KEYRING_RESULT_DENIED;
245    }
246  if (result != GNOME_KEYRING_RESULT_OK)
247    {
248      svn_hash_sets(parameters, "gnome-keyring-opening-failed", "");
249    }
250
251  *done = (result == GNOME_KEYRING_RESULT_OK);
252  return SVN_NO_ERROR;
253}
254
255/* Get cached encrypted credentials from the simple provider's cache. */
256static svn_error_t *
257simple_gnome_keyring_first_creds(void **credentials,
258                                 void **iter_baton,
259                                 void *provider_baton,
260                                 apr_hash_t *parameters,
261                                 const char *realmstring,
262                                 apr_pool_t *pool)
263{
264  return svn_auth__simple_creds_cache_get(credentials,
265                                          iter_baton, provider_baton,
266                                          parameters, realmstring,
267                                          password_get_gnome_keyring,
268                                          SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE,
269                                          pool);
270}
271
272/* Save encrypted credentials to the simple provider's cache. */
273static svn_error_t *
274simple_gnome_keyring_save_creds(svn_boolean_t *saved,
275                                void *credentials,
276                                void *provider_baton,
277                                apr_hash_t *parameters,
278                                const char *realmstring,
279                                apr_pool_t *pool)
280{
281  return svn_auth__simple_creds_cache_set(saved, credentials,
282                                          provider_baton, parameters,
283                                          realmstring,
284                                          password_set_gnome_keyring,
285                                          SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE,
286                                          pool);
287}
288
289#if GLIB_CHECK_VERSION(2,6,0)
290static void
291log_noop(const gchar *log_domain, GLogLevelFlags log_level,
292         const gchar *message, gpointer user_data)
293{
294  /* do nothing */
295}
296#endif
297
298static void
299init_gnome_keyring(void)
300{
301  const char *application_name = NULL;
302  application_name = g_get_application_name();
303  if (!application_name)
304    g_set_application_name("Subversion");
305
306  /* Ideally we call g_log_set_handler() with a log_domain specific to
307     libgnome-keyring.  Unfortunately, at least as of gnome-keyring
308     2.22.3, it doesn't have its own log_domain.  As a result, we
309     suppress stderr spam for not only libgnome-keyring, but for
310     anything else the app is linked to that uses glib logging and
311     doesn't specify a log_domain. */
312#if GLIB_CHECK_VERSION(2,6,0)
313  g_log_set_default_handler(log_noop, NULL);
314#endif
315}
316
317static const svn_auth_provider_t gnome_keyring_simple_provider = {
318  SVN_AUTH_CRED_SIMPLE,
319  simple_gnome_keyring_first_creds,
320  NULL,
321  simple_gnome_keyring_save_creds
322};
323
324/* Public API */
325void
326svn_auth_get_gnome_keyring_simple_provider
327    (svn_auth_provider_object_t **provider,
328     apr_pool_t *pool)
329{
330  svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
331
332  po->vtable = &gnome_keyring_simple_provider;
333  *provider = po;
334
335  init_gnome_keyring();
336}
337
338
339/*-----------------------------------------------------------------------*/
340/* GNOME Keyring SSL client certificate passphrase provider,             */
341/* puts passphrases in GNOME Keyring                                     */
342/*-----------------------------------------------------------------------*/
343
344/* Get cached encrypted credentials from the ssl client cert password
345   provider's cache. */
346static svn_error_t *
347ssl_client_cert_pw_gnome_keyring_first_creds(void **credentials,
348                                             void **iter_baton,
349                                             void *provider_baton,
350                                             apr_hash_t *parameters,
351                                             const char *realmstring,
352                                             apr_pool_t *pool)
353{
354  return svn_auth__ssl_client_cert_pw_cache_get(
355             credentials, iter_baton, provider_baton, parameters, realmstring,
356             password_get_gnome_keyring, SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE,
357             pool);
358}
359
360/* Save encrypted credentials to the ssl client cert password provider's
361   cache. */
362static svn_error_t *
363ssl_client_cert_pw_gnome_keyring_save_creds(svn_boolean_t *saved,
364                                            void *credentials,
365                                            void *provider_baton,
366                                            apr_hash_t *parameters,
367                                            const char *realmstring,
368                                            apr_pool_t *pool)
369{
370  return svn_auth__ssl_client_cert_pw_cache_set(
371             saved, credentials, provider_baton, parameters, realmstring,
372             password_set_gnome_keyring, SVN_AUTH__GNOME_KEYRING_PASSWORD_TYPE,
373             pool);
374}
375
376static const svn_auth_provider_t gnome_keyring_ssl_client_cert_pw_provider = {
377  SVN_AUTH_CRED_SSL_CLIENT_CERT_PW,
378  ssl_client_cert_pw_gnome_keyring_first_creds,
379  NULL,
380  ssl_client_cert_pw_gnome_keyring_save_creds
381};
382
383/* Public API */
384void
385svn_auth_get_gnome_keyring_ssl_client_cert_pw_provider
386    (svn_auth_provider_object_t **provider,
387     apr_pool_t *pool)
388{
389  svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
390
391  po->vtable = &gnome_keyring_ssl_client_cert_pw_provider;
392  *provider = po;
393
394  init_gnome_keyring();
395}
396