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