kwallet.cpp revision 299742
1/* 2 * kwallet.cpp: KWallet 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 <stdlib.h> 31#include <string.h> 32#include <unistd.h> 33 34#include <apr_pools.h> 35#include <apr_strings.h> 36 37#include <dbus/dbus.h> 38#include <QtCore/QCoreApplication> 39#include <QtCore/QString> 40 41#include <kaboutdata.h> 42#include <kcmdlineargs.h> 43#include <kcomponentdata.h> 44#include <klocalizedstring.h> 45#include <kwallet.h> 46 47#include "svn_auth.h" 48#include "svn_config.h" 49#include "svn_error.h" 50#include "svn_hash.h" 51#include "svn_io.h" 52#include "svn_pools.h" 53#include "svn_string.h" 54#include "svn_version.h" 55 56#include "private/svn_auth_private.h" 57 58#include "svn_private_config.h" 59 60 61/*-----------------------------------------------------------------------*/ 62/* KWallet simple provider, puts passwords in KWallet */ 63/*-----------------------------------------------------------------------*/ 64 65static int q_argc = 1; 66static char q_argv0[] = "svn"; // Build non-const char * from string constant 67static char *q_argv[] = { q_argv0 }; 68 69static const char * 70get_application_name(apr_hash_t *parameters, 71 apr_pool_t *pool) 72{ 73 svn_config_t *config = 74 static_cast<svn_config_t *> (apr_hash_get(parameters, 75 SVN_AUTH_PARAM_CONFIG_CATEGORY_CONFIG, 76 APR_HASH_KEY_STRING)); 77 svn_boolean_t svn_application_name_with_pid; 78 svn_config_get_bool(config, 79 &svn_application_name_with_pid, 80 SVN_CONFIG_SECTION_AUTH, 81 SVN_CONFIG_OPTION_KWALLET_SVN_APPLICATION_NAME_WITH_PID, 82 FALSE); 83 const char *svn_application_name; 84 if (svn_application_name_with_pid) 85 { 86 svn_application_name = apr_psprintf(pool, "Subversion [%ld]", long(getpid())); 87 } 88 else 89 { 90 svn_application_name = "Subversion"; 91 } 92 return svn_application_name; 93} 94 95static QString 96get_wallet_name(apr_hash_t *parameters) 97{ 98 svn_config_t *config = 99 static_cast<svn_config_t *> (apr_hash_get(parameters, 100 SVN_AUTH_PARAM_CONFIG_CATEGORY_CONFIG, 101 APR_HASH_KEY_STRING)); 102 const char *wallet_name; 103 svn_config_get(config, 104 &wallet_name, 105 SVN_CONFIG_SECTION_AUTH, 106 SVN_CONFIG_OPTION_KWALLET_WALLET, 107 ""); 108 if (strcmp(wallet_name, "") == 0) 109 { 110 return KWallet::Wallet::NetworkWallet(); 111 } 112 else 113 { 114 return QString::fromUtf8(wallet_name); 115 } 116} 117 118static WId 119get_wid(void) 120{ 121 WId wid = 1; 122 const char *wid_env_string = getenv("WINDOWID"); 123 124 if (wid_env_string) 125 { 126 apr_int64_t wid_env; 127 svn_error_t *err; 128 129 err = svn_cstring_atoi64(&wid_env, wid_env_string); 130 if (err) 131 svn_error_clear(err); 132 else 133 wid = (WId)wid_env; 134 } 135 136 return wid; 137} 138 139/* Forward definition */ 140static apr_status_t 141kwallet_terminate(void *data); 142 143static KWallet::Wallet * 144get_wallet(QString wallet_name, 145 apr_hash_t *parameters) 146{ 147 KWallet::Wallet *wallet = 148 static_cast<KWallet::Wallet *> (svn_hash_gets(parameters, 149 "kwallet-wallet")); 150 if (! wallet && ! svn_hash_gets(parameters, "kwallet-opening-failed")) 151 { 152 wallet = KWallet::Wallet::openWallet(wallet_name, get_wid(), 153 KWallet::Wallet::Synchronous); 154 155 if (wallet) 156 { 157 svn_hash_sets(parameters, "kwallet-wallet", wallet); 158 159 apr_pool_cleanup_register(apr_hash_pool_get(parameters), 160 parameters, kwallet_terminate, 161 apr_pool_cleanup_null); 162 163 svn_hash_sets(parameters, "kwallet-initialized", ""); 164 } 165 else 166 { 167 svn_hash_sets(parameters, "kwallet-opening-failed", ""); 168 } 169 } 170 return wallet; 171} 172 173static apr_status_t 174kwallet_terminate(void *data) 175{ 176 apr_hash_t *parameters = static_cast<apr_hash_t *> (data); 177 if (svn_hash_gets(parameters, "kwallet-initialized")) 178 { 179 KWallet::Wallet *wallet = get_wallet(NULL, parameters); 180 delete wallet; 181 svn_hash_sets(parameters, "kwallet-wallet", NULL); 182 svn_hash_sets(parameters, "kwallet-initialized", NULL); 183 } 184 return APR_SUCCESS; 185} 186 187/* Implementation of svn_auth__password_get_t that retrieves 188 the password from KWallet. */ 189static svn_error_t * 190kwallet_password_get(svn_boolean_t *done, 191 const char **password, 192 apr_hash_t *creds, 193 const char *realmstring, 194 const char *username, 195 apr_hash_t *parameters, 196 svn_boolean_t non_interactive, 197 apr_pool_t *pool) 198{ 199 QString wallet_name = get_wallet_name(parameters); 200 201 *done = FALSE; 202 203 if (! dbus_bus_get(DBUS_BUS_SESSION, NULL)) 204 { 205 return SVN_NO_ERROR; 206 } 207 208 if (non_interactive) 209 { 210 if (!KWallet::Wallet::isOpen(wallet_name)) 211 return SVN_NO_ERROR; 212 213 /* There is a race here: the wallet was open just now, but will 214 it still be open when we come to use it below? */ 215 } 216 217 QCoreApplication *app; 218 if (! qApp) 219 { 220 int argc = q_argc; 221 app = new QCoreApplication(argc, q_argv); 222 } 223 224 KCmdLineArgs::init(q_argc, q_argv, 225 get_application_name(parameters, pool), 226 "subversion", 227 ki18n(get_application_name(parameters, pool)), 228 SVN_VER_NUMBER, 229 ki18n("Version control system"), 230 KCmdLineArgs::CmdLineArgKDE); 231 KComponentData component_data(KCmdLineArgs::aboutData()); 232 QString folder = QString::fromUtf8("Subversion"); 233 QString key = 234 QString::fromUtf8(username) + "@" + QString::fromUtf8(realmstring); 235 if (! KWallet::Wallet::keyDoesNotExist(wallet_name, folder, key)) 236 { 237 KWallet::Wallet *wallet = get_wallet(wallet_name, parameters); 238 if (wallet) 239 { 240 if (wallet->setFolder(folder)) 241 { 242 QString q_password; 243 if (wallet->readPassword(key, q_password) == 0) 244 { 245 *password = apr_pstrmemdup(pool, 246 q_password.toUtf8().data(), 247 q_password.size()); 248 *done = TRUE; 249 } 250 } 251 } 252 } 253 254 return SVN_NO_ERROR; 255} 256 257/* Implementation of svn_auth__password_set_t that stores 258 the password in KWallet. */ 259static svn_error_t * 260kwallet_password_set(svn_boolean_t *done, 261 apr_hash_t *creds, 262 const char *realmstring, 263 const char *username, 264 const char *password, 265 apr_hash_t *parameters, 266 svn_boolean_t non_interactive, 267 apr_pool_t *pool) 268{ 269 QString wallet_name = get_wallet_name(parameters); 270 271 *done = FALSE; 272 273 if (! dbus_bus_get(DBUS_BUS_SESSION, NULL)) 274 { 275 return SVN_NO_ERROR; 276 } 277 278 if (non_interactive) 279 { 280 if (!KWallet::Wallet::isOpen(wallet_name)) 281 return SVN_NO_ERROR; 282 283 /* There is a race here: the wallet was open just now, but will 284 it still be open when we come to use it below? */ 285 } 286 287 QCoreApplication *app; 288 if (! qApp) 289 { 290 int argc = q_argc; 291 app = new QCoreApplication(argc, q_argv); 292 } 293 294 KCmdLineArgs::init(q_argc, q_argv, 295 get_application_name(parameters, pool), 296 "subversion", 297 ki18n(get_application_name(parameters, pool)), 298 SVN_VER_NUMBER, 299 ki18n("Version control system"), 300 KCmdLineArgs::CmdLineArgKDE); 301 KComponentData component_data(KCmdLineArgs::aboutData()); 302 QString q_password = QString::fromUtf8(password); 303 QString folder = QString::fromUtf8("Subversion"); 304 KWallet::Wallet *wallet = get_wallet(wallet_name, parameters); 305 if (wallet) 306 { 307 if (! wallet->hasFolder(folder)) 308 { 309 wallet->createFolder(folder); 310 } 311 if (wallet->setFolder(folder)) 312 { 313 QString key = QString::fromUtf8(username) + "@" 314 + QString::fromUtf8(realmstring); 315 if (wallet->writePassword(key, q_password) == 0) 316 { 317 *done = TRUE; 318 } 319 } 320 } 321 322 return SVN_NO_ERROR; 323} 324 325/* Get cached encrypted credentials from the simple provider's cache. */ 326static svn_error_t * 327kwallet_simple_first_creds(void **credentials, 328 void **iter_baton, 329 void *provider_baton, 330 apr_hash_t *parameters, 331 const char *realmstring, 332 apr_pool_t *pool) 333{ 334 return svn_auth__simple_creds_cache_get(credentials, 335 iter_baton, 336 provider_baton, 337 parameters, 338 realmstring, 339 kwallet_password_get, 340 SVN_AUTH__KWALLET_PASSWORD_TYPE, 341 pool); 342} 343 344/* Save encrypted credentials to the simple provider's cache. */ 345static svn_error_t * 346kwallet_simple_save_creds(svn_boolean_t *saved, 347 void *credentials, 348 void *provider_baton, 349 apr_hash_t *parameters, 350 const char *realmstring, 351 apr_pool_t *pool) 352{ 353 return svn_auth__simple_creds_cache_set(saved, credentials, 354 provider_baton, 355 parameters, 356 realmstring, 357 kwallet_password_set, 358 SVN_AUTH__KWALLET_PASSWORD_TYPE, 359 pool); 360} 361 362static const svn_auth_provider_t kwallet_simple_provider = { 363 SVN_AUTH_CRED_SIMPLE, 364 kwallet_simple_first_creds, 365 NULL, 366 kwallet_simple_save_creds 367}; 368 369/* Public API */ 370extern "C" { 371void 372svn_auth_get_kwallet_simple_provider(svn_auth_provider_object_t **provider, 373 apr_pool_t *pool) 374{ 375 svn_auth_provider_object_t *po = 376 static_cast<svn_auth_provider_object_t *> (apr_pcalloc(pool, sizeof(*po))); 377 378 po->vtable = &kwallet_simple_provider; 379 *provider = po; 380} 381} 382 383 384/*-----------------------------------------------------------------------*/ 385/* KWallet SSL client certificate passphrase provider, */ 386/* puts passphrases in KWallet */ 387/*-----------------------------------------------------------------------*/ 388 389/* Get cached encrypted credentials from the ssl client cert password 390 provider's cache. */ 391static svn_error_t * 392kwallet_ssl_client_cert_pw_first_creds(void **credentials, 393 void **iter_baton, 394 void *provider_baton, 395 apr_hash_t *parameters, 396 const char *realmstring, 397 apr_pool_t *pool) 398{ 399 return svn_auth__ssl_client_cert_pw_cache_get(credentials, 400 iter_baton, provider_baton, 401 parameters, realmstring, 402 kwallet_password_get, 403 SVN_AUTH__KWALLET_PASSWORD_TYPE, 404 pool); 405} 406 407/* Save encrypted credentials to the ssl client cert password provider's 408 cache. */ 409static svn_error_t * 410kwallet_ssl_client_cert_pw_save_creds(svn_boolean_t *saved, 411 void *credentials, 412 void *provider_baton, 413 apr_hash_t *parameters, 414 const char *realmstring, 415 apr_pool_t *pool) 416{ 417 return svn_auth__ssl_client_cert_pw_cache_set(saved, credentials, 418 provider_baton, parameters, 419 realmstring, 420 kwallet_password_set, 421 SVN_AUTH__KWALLET_PASSWORD_TYPE, 422 pool); 423} 424 425static const svn_auth_provider_t kwallet_ssl_client_cert_pw_provider = { 426 SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, 427 kwallet_ssl_client_cert_pw_first_creds, 428 NULL, 429 kwallet_ssl_client_cert_pw_save_creds 430}; 431 432/* Public API */ 433extern "C" { 434void 435svn_auth_get_kwallet_ssl_client_cert_pw_provider 436 (svn_auth_provider_object_t **provider, 437 apr_pool_t *pool) 438{ 439 svn_auth_provider_object_t *po = 440 static_cast<svn_auth_provider_object_t *> (apr_pcalloc(pool, sizeof(*po))); 441 442 po->vtable = &kwallet_ssl_client_cert_pw_provider; 443 *provider = po; 444} 445} 446