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