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