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