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