1251881Speter/*
2251881Speter * kwallet.cpp: KWallet provider for SVN_AUTH_CRED_*
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter/* ==================================================================== */
25251881Speter
26251881Speter
27251881Speter
28251881Speter/*** Includes. ***/
29251881Speter
30251881Speter#include <stdlib.h>
31251881Speter#include <string.h>
32251881Speter#include <unistd.h>
33251881Speter
34251881Speter#include <apr_pools.h>
35251881Speter#include <apr_strings.h>
36251881Speter
37251881Speter#include <dbus/dbus.h>
38251881Speter#include <QtCore/QCoreApplication>
39251881Speter#include <QtCore/QString>
40251881Speter
41251881Speter#include <kaboutdata.h>
42251881Speter#include <klocalizedstring.h>
43251881Speter#include <kwallet.h>
44251881Speter
45251881Speter#include "svn_auth.h"
46251881Speter#include "svn_config.h"
47251881Speter#include "svn_error.h"
48289180Speter#include "svn_hash.h"
49251881Speter#include "svn_io.h"
50251881Speter#include "svn_pools.h"
51251881Speter#include "svn_string.h"
52251881Speter#include "svn_version.h"
53251881Speter
54251881Speter#include "private/svn_auth_private.h"
55251881Speter
56251881Speter#include "svn_private_config.h"
57251881Speter
58362181Sdim#ifndef SVN_HAVE_KF5
59362181Sdim#include <kcmdlineargs.h>
60362181Sdim#include <kcomponentdata.h>
61362181Sdim#endif
62251881Speter
63251881Speter/*-----------------------------------------------------------------------*/
64251881Speter/* KWallet simple provider, puts passwords in KWallet                    */
65251881Speter/*-----------------------------------------------------------------------*/
66251881Speter
67251881Speterstatic int q_argc = 1;
68251881Speterstatic char q_argv0[] = "svn"; // Build non-const char * from string constant
69251881Speterstatic char *q_argv[] = { q_argv0 };
70251881Speter
71251881Speterstatic const char *
72251881Speterget_application_name(apr_hash_t *parameters,
73251881Speter                     apr_pool_t *pool)
74251881Speter{
75251881Speter  svn_config_t *config =
76251881Speter    static_cast<svn_config_t *> (apr_hash_get(parameters,
77251881Speter                                              SVN_AUTH_PARAM_CONFIG_CATEGORY_CONFIG,
78251881Speter                                              APR_HASH_KEY_STRING));
79251881Speter  svn_boolean_t svn_application_name_with_pid;
80251881Speter  svn_config_get_bool(config,
81251881Speter                      &svn_application_name_with_pid,
82251881Speter                      SVN_CONFIG_SECTION_AUTH,
83251881Speter                      SVN_CONFIG_OPTION_KWALLET_SVN_APPLICATION_NAME_WITH_PID,
84251881Speter                      FALSE);
85251881Speter  const char *svn_application_name;
86251881Speter  if (svn_application_name_with_pid)
87251881Speter    {
88251881Speter      svn_application_name = apr_psprintf(pool, "Subversion [%ld]", long(getpid()));
89251881Speter    }
90251881Speter  else
91251881Speter    {
92251881Speter      svn_application_name = "Subversion";
93251881Speter    }
94251881Speter  return svn_application_name;
95251881Speter}
96251881Speter
97251881Speterstatic QString
98251881Speterget_wallet_name(apr_hash_t *parameters)
99251881Speter{
100251881Speter  svn_config_t *config =
101251881Speter    static_cast<svn_config_t *> (apr_hash_get(parameters,
102251881Speter                                              SVN_AUTH_PARAM_CONFIG_CATEGORY_CONFIG,
103251881Speter                                              APR_HASH_KEY_STRING));
104251881Speter  const char *wallet_name;
105251881Speter  svn_config_get(config,
106251881Speter                 &wallet_name,
107251881Speter                 SVN_CONFIG_SECTION_AUTH,
108251881Speter                 SVN_CONFIG_OPTION_KWALLET_WALLET,
109251881Speter                 "");
110251881Speter  if (strcmp(wallet_name, "") == 0)
111251881Speter    {
112251881Speter      return KWallet::Wallet::NetworkWallet();
113251881Speter    }
114251881Speter  else
115251881Speter    {
116251881Speter      return QString::fromUtf8(wallet_name);
117251881Speter    }
118251881Speter}
119251881Speter
120251881Speterstatic WId
121251881Speterget_wid(void)
122251881Speter{
123251881Speter  WId wid = 1;
124251881Speter  const char *wid_env_string = getenv("WINDOWID");
125251881Speter
126251881Speter  if (wid_env_string)
127251881Speter    {
128251881Speter      apr_int64_t wid_env;
129251881Speter      svn_error_t *err;
130251881Speter
131251881Speter      err = svn_cstring_atoi64(&wid_env, wid_env_string);
132251881Speter      if (err)
133251881Speter        svn_error_clear(err);
134251881Speter      else
135251881Speter        wid = (WId)wid_env;
136251881Speter    }
137251881Speter
138251881Speter  return wid;
139251881Speter}
140251881Speter
141289180Speter/* Forward definition */
142289180Speterstatic apr_status_t
143289180Speterkwallet_terminate(void *data);
144289180Speter
145251881Speterstatic KWallet::Wallet *
146251881Speterget_wallet(QString wallet_name,
147251881Speter           apr_hash_t *parameters)
148251881Speter{
149251881Speter  KWallet::Wallet *wallet =
150289180Speter    static_cast<KWallet::Wallet *> (svn_hash_gets(parameters,
151289180Speter                                                  "kwallet-wallet"));
152289180Speter  if (! wallet && ! svn_hash_gets(parameters, "kwallet-opening-failed"))
153251881Speter    {
154251881Speter      wallet = KWallet::Wallet::openWallet(wallet_name, get_wid(),
155251881Speter                                           KWallet::Wallet::Synchronous);
156289180Speter
157289180Speter      if (wallet)
158289180Speter        {
159289180Speter          svn_hash_sets(parameters, "kwallet-wallet", wallet);
160289180Speter
161289180Speter          apr_pool_cleanup_register(apr_hash_pool_get(parameters),
162289180Speter                                    parameters, kwallet_terminate,
163289180Speter                                    apr_pool_cleanup_null);
164289180Speter
165289180Speter          svn_hash_sets(parameters, "kwallet-initialized", "");
166289180Speter        }
167289180Speter      else
168289180Speter        {
169289180Speter          svn_hash_sets(parameters, "kwallet-opening-failed", "");
170289180Speter        }
171251881Speter    }
172251881Speter  return wallet;
173251881Speter}
174251881Speter
175251881Speterstatic apr_status_t
176251881Speterkwallet_terminate(void *data)
177251881Speter{
178251881Speter  apr_hash_t *parameters = static_cast<apr_hash_t *> (data);
179289180Speter  if (svn_hash_gets(parameters, "kwallet-initialized"))
180251881Speter    {
181251881Speter      KWallet::Wallet *wallet = get_wallet(NULL, parameters);
182251881Speter      delete wallet;
183289180Speter      svn_hash_sets(parameters, "kwallet-wallet", NULL);
184289180Speter      svn_hash_sets(parameters, "kwallet-initialized", NULL);
185251881Speter    }
186251881Speter  return APR_SUCCESS;
187251881Speter}
188251881Speter
189251881Speter/* Implementation of svn_auth__password_get_t that retrieves
190251881Speter   the password from KWallet. */
191251881Speterstatic svn_error_t *
192251881Speterkwallet_password_get(svn_boolean_t *done,
193251881Speter                     const char **password,
194251881Speter                     apr_hash_t *creds,
195251881Speter                     const char *realmstring,
196251881Speter                     const char *username,
197251881Speter                     apr_hash_t *parameters,
198251881Speter                     svn_boolean_t non_interactive,
199251881Speter                     apr_pool_t *pool)
200251881Speter{
201251881Speter  QString wallet_name = get_wallet_name(parameters);
202251881Speter
203251881Speter  *done = FALSE;
204251881Speter
205251881Speter  if (! dbus_bus_get(DBUS_BUS_SESSION, NULL))
206251881Speter    {
207251881Speter      return SVN_NO_ERROR;
208251881Speter    }
209251881Speter
210251881Speter  if (non_interactive)
211251881Speter    {
212251881Speter      if (!KWallet::Wallet::isOpen(wallet_name))
213251881Speter        return SVN_NO_ERROR;
214251881Speter
215251881Speter      /* There is a race here: the wallet was open just now, but will
216251881Speter         it still be open when we come to use it below? */
217251881Speter    }
218251881Speter
219251881Speter  QCoreApplication *app;
220251881Speter  if (! qApp)
221251881Speter    {
222251881Speter      int argc = q_argc;
223251881Speter      app = new QCoreApplication(argc, q_argv);
224251881Speter    }
225251881Speter
226362181Sdim#if SVN_HAVE_KF5
227362181Sdim  KLocalizedString::setApplicationDomain("subversion"); /* translation domain */
228362181Sdim
229362181Sdim  /* componentName appears in KDE GUI prompts */
230362181Sdim  KAboutData aboutData(QString("subversion"),            /* componentName */
231362181Sdim                       i18n(get_application_name(parameters,
232362181Sdim                                                 pool)), /* displayName */
233362181Sdim                       QString(SVN_VER_NUMBER));
234362181Sdim  KAboutData::setApplicationData(aboutData);
235362181Sdim#else
236251881Speter  KCmdLineArgs::init(q_argc, q_argv,
237251881Speter                     get_application_name(parameters, pool),
238251881Speter                     "subversion",
239251881Speter                     ki18n(get_application_name(parameters, pool)),
240251881Speter                     SVN_VER_NUMBER,
241251881Speter                     ki18n("Version control system"),
242251881Speter                     KCmdLineArgs::CmdLineArgKDE);
243251881Speter  KComponentData component_data(KCmdLineArgs::aboutData());
244362181Sdim#endif
245362181Sdim
246251881Speter  QString folder = QString::fromUtf8("Subversion");
247251881Speter  QString key =
248251881Speter    QString::fromUtf8(username) + "@" + QString::fromUtf8(realmstring);
249251881Speter  if (! KWallet::Wallet::keyDoesNotExist(wallet_name, folder, key))
250251881Speter    {
251251881Speter      KWallet::Wallet *wallet = get_wallet(wallet_name, parameters);
252251881Speter      if (wallet)
253251881Speter        {
254251881Speter          if (wallet->setFolder(folder))
255251881Speter            {
256251881Speter              QString q_password;
257251881Speter              if (wallet->readPassword(key, q_password) == 0)
258251881Speter                {
259251881Speter                  *password = apr_pstrmemdup(pool,
260251881Speter                                             q_password.toUtf8().data(),
261251881Speter                                             q_password.size());
262251881Speter                  *done = TRUE;
263251881Speter                }
264251881Speter            }
265251881Speter        }
266251881Speter    }
267251881Speter
268251881Speter  return SVN_NO_ERROR;
269251881Speter}
270251881Speter
271251881Speter/* Implementation of svn_auth__password_set_t that stores
272251881Speter   the password in KWallet. */
273251881Speterstatic svn_error_t *
274251881Speterkwallet_password_set(svn_boolean_t *done,
275251881Speter                     apr_hash_t *creds,
276251881Speter                     const char *realmstring,
277251881Speter                     const char *username,
278251881Speter                     const char *password,
279251881Speter                     apr_hash_t *parameters,
280251881Speter                     svn_boolean_t non_interactive,
281251881Speter                     apr_pool_t *pool)
282251881Speter{
283251881Speter  QString wallet_name = get_wallet_name(parameters);
284251881Speter
285251881Speter  *done = FALSE;
286251881Speter
287251881Speter  if (! dbus_bus_get(DBUS_BUS_SESSION, NULL))
288251881Speter    {
289251881Speter      return SVN_NO_ERROR;
290251881Speter    }
291251881Speter
292251881Speter  if (non_interactive)
293251881Speter    {
294251881Speter      if (!KWallet::Wallet::isOpen(wallet_name))
295251881Speter        return SVN_NO_ERROR;
296251881Speter
297251881Speter      /* There is a race here: the wallet was open just now, but will
298251881Speter         it still be open when we come to use it below? */
299251881Speter    }
300251881Speter
301251881Speter  QCoreApplication *app;
302251881Speter  if (! qApp)
303251881Speter    {
304251881Speter      int argc = q_argc;
305251881Speter      app = new QCoreApplication(argc, q_argv);
306251881Speter    }
307251881Speter
308362181Sdim#if SVN_HAVE_KF5
309362181Sdim  KLocalizedString::setApplicationDomain("subversion"); /* translation domain */
310362181Sdim
311362181Sdim  /* componentName appears in KDE GUI prompts */
312362181Sdim  KAboutData aboutData(QString("subversion"),            /* componentName */
313362181Sdim                       i18n(get_application_name(parameters,
314362181Sdim                                                 pool)), /* displayName */
315362181Sdim                       QString(SVN_VER_NUMBER));
316362181Sdim  KAboutData::setApplicationData(aboutData);
317362181Sdim#else
318251881Speter  KCmdLineArgs::init(q_argc, q_argv,
319251881Speter                     get_application_name(parameters, pool),
320251881Speter                     "subversion",
321251881Speter                     ki18n(get_application_name(parameters, pool)),
322251881Speter                     SVN_VER_NUMBER,
323251881Speter                     ki18n("Version control system"),
324251881Speter                     KCmdLineArgs::CmdLineArgKDE);
325251881Speter  KComponentData component_data(KCmdLineArgs::aboutData());
326362181Sdim#endif
327362181Sdim
328251881Speter  QString q_password = QString::fromUtf8(password);
329251881Speter  QString folder = QString::fromUtf8("Subversion");
330251881Speter  KWallet::Wallet *wallet = get_wallet(wallet_name, parameters);
331251881Speter  if (wallet)
332251881Speter    {
333251881Speter      if (! wallet->hasFolder(folder))
334251881Speter        {
335251881Speter          wallet->createFolder(folder);
336251881Speter        }
337251881Speter      if (wallet->setFolder(folder))
338251881Speter        {
339251881Speter          QString key = QString::fromUtf8(username) + "@"
340251881Speter            + QString::fromUtf8(realmstring);
341251881Speter          if (wallet->writePassword(key, q_password) == 0)
342251881Speter            {
343251881Speter              *done = TRUE;
344251881Speter            }
345251881Speter        }
346251881Speter    }
347251881Speter
348251881Speter  return SVN_NO_ERROR;
349251881Speter}
350251881Speter
351251881Speter/* Get cached encrypted credentials from the simple provider's cache. */
352251881Speterstatic svn_error_t *
353251881Speterkwallet_simple_first_creds(void **credentials,
354251881Speter                           void **iter_baton,
355251881Speter                           void *provider_baton,
356251881Speter                           apr_hash_t *parameters,
357251881Speter                           const char *realmstring,
358251881Speter                           apr_pool_t *pool)
359251881Speter{
360251881Speter  return svn_auth__simple_creds_cache_get(credentials,
361251881Speter                                          iter_baton,
362251881Speter                                          provider_baton,
363251881Speter                                          parameters,
364251881Speter                                          realmstring,
365251881Speter                                          kwallet_password_get,
366251881Speter                                          SVN_AUTH__KWALLET_PASSWORD_TYPE,
367251881Speter                                          pool);
368251881Speter}
369251881Speter
370251881Speter/* Save encrypted credentials to the simple provider's cache. */
371251881Speterstatic svn_error_t *
372251881Speterkwallet_simple_save_creds(svn_boolean_t *saved,
373251881Speter                          void *credentials,
374251881Speter                          void *provider_baton,
375251881Speter                          apr_hash_t *parameters,
376251881Speter                          const char *realmstring,
377251881Speter                          apr_pool_t *pool)
378251881Speter{
379251881Speter  return svn_auth__simple_creds_cache_set(saved, credentials,
380251881Speter                                          provider_baton,
381251881Speter                                          parameters,
382251881Speter                                          realmstring,
383251881Speter                                          kwallet_password_set,
384251881Speter                                          SVN_AUTH__KWALLET_PASSWORD_TYPE,
385251881Speter                                          pool);
386251881Speter}
387251881Speter
388251881Speterstatic const svn_auth_provider_t kwallet_simple_provider = {
389251881Speter  SVN_AUTH_CRED_SIMPLE,
390251881Speter  kwallet_simple_first_creds,
391251881Speter  NULL,
392251881Speter  kwallet_simple_save_creds
393251881Speter};
394251881Speter
395251881Speter/* Public API */
396251881Speterextern "C" {
397251881Spetervoid
398251881Spetersvn_auth_get_kwallet_simple_provider(svn_auth_provider_object_t **provider,
399251881Speter                                     apr_pool_t *pool)
400251881Speter{
401251881Speter  svn_auth_provider_object_t *po =
402251881Speter    static_cast<svn_auth_provider_object_t *> (apr_pcalloc(pool, sizeof(*po)));
403251881Speter
404251881Speter  po->vtable = &kwallet_simple_provider;
405251881Speter  *provider = po;
406251881Speter}
407251881Speter}
408251881Speter
409251881Speter
410251881Speter/*-----------------------------------------------------------------------*/
411251881Speter/* KWallet SSL client certificate passphrase provider,                   */
412251881Speter/* puts passphrases in KWallet                                           */
413251881Speter/*-----------------------------------------------------------------------*/
414251881Speter
415251881Speter/* Get cached encrypted credentials from the ssl client cert password
416251881Speter   provider's cache. */
417251881Speterstatic svn_error_t *
418251881Speterkwallet_ssl_client_cert_pw_first_creds(void **credentials,
419251881Speter                                       void **iter_baton,
420251881Speter                                       void *provider_baton,
421251881Speter                                       apr_hash_t *parameters,
422251881Speter                                       const char *realmstring,
423251881Speter                                       apr_pool_t *pool)
424251881Speter{
425251881Speter  return svn_auth__ssl_client_cert_pw_cache_get(credentials,
426251881Speter                                                iter_baton, provider_baton,
427251881Speter                                                parameters, realmstring,
428251881Speter                                                kwallet_password_get,
429251881Speter                                                SVN_AUTH__KWALLET_PASSWORD_TYPE,
430251881Speter                                                pool);
431251881Speter}
432251881Speter
433251881Speter/* Save encrypted credentials to the ssl client cert password provider's
434251881Speter   cache. */
435251881Speterstatic svn_error_t *
436251881Speterkwallet_ssl_client_cert_pw_save_creds(svn_boolean_t *saved,
437251881Speter                                      void *credentials,
438251881Speter                                      void *provider_baton,
439251881Speter                                      apr_hash_t *parameters,
440251881Speter                                      const char *realmstring,
441251881Speter                                      apr_pool_t *pool)
442251881Speter{
443251881Speter  return svn_auth__ssl_client_cert_pw_cache_set(saved, credentials,
444251881Speter                                                provider_baton, parameters,
445251881Speter                                                realmstring,
446251881Speter                                                kwallet_password_set,
447251881Speter                                                SVN_AUTH__KWALLET_PASSWORD_TYPE,
448251881Speter                                                pool);
449251881Speter}
450251881Speter
451251881Speterstatic const svn_auth_provider_t kwallet_ssl_client_cert_pw_provider = {
452251881Speter  SVN_AUTH_CRED_SSL_CLIENT_CERT_PW,
453251881Speter  kwallet_ssl_client_cert_pw_first_creds,
454251881Speter  NULL,
455251881Speter  kwallet_ssl_client_cert_pw_save_creds
456251881Speter};
457251881Speter
458251881Speter/* Public API */
459251881Speterextern "C" {
460251881Spetervoid
461251881Spetersvn_auth_get_kwallet_ssl_client_cert_pw_provider
462251881Speter    (svn_auth_provider_object_t **provider,
463251881Speter     apr_pool_t *pool)
464251881Speter{
465251881Speter  svn_auth_provider_object_t *po =
466251881Speter    static_cast<svn_auth_provider_object_t *> (apr_pcalloc(pool, sizeof(*po)));
467251881Speter
468251881Speter  po->vtable = &kwallet_ssl_client_cert_pw_provider;
469251881Speter  *provider = po;
470251881Speter}
471251881Speter}
472