]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_auth_gnome_keyring/gnome_keyring.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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
27 \f
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 \f
47 /*-----------------------------------------------------------------------*/
48 /* GNOME Keyring simple provider, puts passwords in GNOME Keyring        */
49 /*-----------------------------------------------------------------------*/
50
51
52 struct 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. */
61 static void
62 callback_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. */
83 static void
84 callback_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. */
95 static void
96 callback_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. */
121 static void
122 callback_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. */
144 static char*
145 get_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. */
171 static svn_boolean_t
172 check_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. */
200 static svn_boolean_t
201 unlock_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? */
238 static svn_error_t *
239 ensure_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. */
284 static svn_error_t *
285 password_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. */
339 static svn_error_t *
340 password_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. */
378 static svn_error_t *
379 simple_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. */
395 static svn_error_t *
396 simple_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)
412 static void
413 log_noop(const gchar *log_domain, GLogLevelFlags log_level,
414          const gchar *message, gpointer user_data)
415 {
416   /* do nothing */
417 }
418 #endif
419
420 static void
421 init_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
439 static 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 */
447 void
448 svn_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 \f
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. */
468 static svn_error_t *
469 ssl_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. */
484 static svn_error_t *
485 ssl_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
498 static 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 */
506 void
507 svn_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 }