1/*
2 * auth.c: authentication support functions for Subversion
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#include <apr_pools.h>
26#include <apr_tables.h>
27#include <apr_strings.h>
28
29#include "svn_hash.h"
30#include "svn_types.h"
31#include "svn_string.h"
32#include "svn_error.h"
33#include "svn_auth.h"
34#include "svn_config.h"
35#include "svn_private_config.h"
36#include "svn_dso.h"
37#include "svn_version.h"
38#include "private/svn_auth_private.h"
39#include "private/svn_dep_compat.h"
40
41#include "auth.h"
42
43/* AN OVERVIEW
44   ===========
45
46   A good way to think of this machinery is as a set of tables.
47
48     - Each type of credentials selects a single table.
49
50     - In a given table, each row is a 'provider' capable of returning
51       the same type of credentials.  Each column represents a
52       provider's repeated attempts to provide credentials.
53
54
55   Fetching Credentials from Providers
56   -----------------------------------
57
58   When the caller asks for a particular type of credentials, the
59   machinery in this file walks over the appropriate table.  It starts
60   with the first provider (first row), and calls first_credentials()
61   to get the first set of credentials (first column).  If the caller
62   is unhappy with the credentials, then each subsequent call to
63   next_credentials() traverses the row from left to right.  If the
64   provider returns error at any point, then we go to the next provider
65   (row).  We continue this way until every provider fails, or
66   until the client is happy with the returned credentials.
67
68   Note that the caller cannot see the table traversal, and thus has
69   no idea when we switch providers.
70
71
72   Storing Credentials with Providers
73   ----------------------------------
74
75   When the server has validated a set of credentials, and when
76   credential caching is enabled, we have the chance to store those
77   credentials for later use.  The provider which provided the working
78   credentials is the first one given the opportunity to (re)cache
79   those credentials.  Its save_credentials() function is invoked with
80   the working credentials.  If that provider reports that it
81   successfully stored the credentials, we're done.  Otherwise, we
82   walk the providers (rows) for that type of credentials in order
83   from the top of the table, allowing each in turn the opportunity to
84   store the credentials.  When one reports that it has done so
85   successfully -- or when we run out of providers (rows) to try --
86   the table walk ends.
87*/
88
89
90
91/* This effectively defines a single table.  Every provider in this
92   array returns the same kind of credentials. */
93typedef struct provider_set_t
94{
95  /* ordered array of svn_auth_provider_object_t */
96  apr_array_header_t *providers;
97
98} provider_set_t;
99
100
101/* The main auth baton. */
102struct svn_auth_baton_t
103{
104  /* a collection of tables.  maps cred_kind -> provider_set */
105  apr_hash_t *tables;
106
107  /* the pool I'm allocated in. */
108  apr_pool_t *pool;
109
110  /* run-time parameters needed by providers. */
111  apr_hash_t *parameters;
112  apr_hash_t *slave_parameters;
113
114  /* run-time credentials cache. */
115  apr_hash_t *creds_cache;
116};
117
118/* Abstracted iteration baton */
119struct svn_auth_iterstate_t
120{
121  provider_set_t *table;        /* the table being searched */
122  int provider_idx;             /* the current provider (row) */
123  svn_boolean_t got_first;      /* did we get the provider's first creds? */
124  void *provider_iter_baton;    /* the provider's own iteration context */
125  const char *realmstring;      /* The original realmstring passed in */
126  const char *cache_key;        /* key to use in auth_baton's creds_cache */
127  svn_auth_baton_t *auth_baton; /* the original auth_baton. */
128  apr_hash_t *parameters;
129};
130
131
132
133void
134svn_auth_open(svn_auth_baton_t **auth_baton,
135              const apr_array_header_t *providers,
136              apr_pool_t *pool)
137{
138  svn_auth_baton_t *ab;
139  svn_auth_provider_object_t *provider;
140  int i;
141
142  /* Build the auth_baton. */
143  ab = apr_pcalloc(pool, sizeof(*ab));
144  ab->tables = apr_hash_make(pool);
145  ab->parameters = apr_hash_make(pool);
146  /* ab->slave_parameters = NULL; */
147  ab->creds_cache = apr_hash_make(pool);
148  ab->pool = pool;
149
150  /* Register each provider in order.  Providers of different
151     credentials will be automatically sorted into different tables by
152     register_provider(). */
153  for (i = 0; i < providers->nelts; i++)
154    {
155      provider_set_t *table;
156      provider = APR_ARRAY_IDX(providers, i, svn_auth_provider_object_t *);
157
158      /* Add it to the appropriate table in the auth_baton */
159      table = svn_hash_gets(ab->tables, provider->vtable->cred_kind);
160      if (! table)
161        {
162          table = apr_pcalloc(pool, sizeof(*table));
163          table->providers
164            = apr_array_make(pool, 1, sizeof(svn_auth_provider_object_t *));
165
166          svn_hash_sets(ab->tables, provider->vtable->cred_kind, table);
167        }
168      APR_ARRAY_PUSH(table->providers, svn_auth_provider_object_t *)
169        = provider;
170    }
171
172  *auth_baton = ab;
173}
174
175/* Magic pointer value to allow storing 'NULL' in an apr_hash_t */
176static const void *auth_NULL = NULL;
177
178void
179svn_auth_set_parameter(svn_auth_baton_t *auth_baton,
180                       const char *name,
181                       const void *value)
182{
183  if (auth_baton)
184    {
185      if (auth_baton->slave_parameters)
186        {
187          if (!value)
188            value = &auth_NULL;
189
190          svn_hash_sets(auth_baton->slave_parameters, name, value);
191        }
192      else
193        svn_hash_sets(auth_baton->parameters, name, value);
194    }
195}
196
197const void *
198svn_auth_get_parameter(svn_auth_baton_t *auth_baton,
199                       const char *name)
200{
201  const void *value;
202  if (!auth_baton)
203    return NULL;
204  else if (!auth_baton->slave_parameters)
205    return svn_hash_gets(auth_baton->parameters, name);
206
207  value = svn_hash_gets(auth_baton->slave_parameters, name);
208
209  if (value)
210    return (value == &auth_NULL ? NULL : value);
211
212  return svn_hash_gets(auth_baton->parameters, name);
213}
214
215
216/* Return the key used to address the in-memory cache of auth
217   credentials of type CRED_KIND and associated with REALMSTRING. */
218static const char *
219make_cache_key(const char *cred_kind,
220               const char *realmstring,
221               apr_pool_t *pool)
222{
223  return apr_pstrcat(pool, cred_kind, ":", realmstring, SVN_VA_NULL);
224}
225
226svn_error_t *
227svn_auth_first_credentials(void **credentials,
228                           svn_auth_iterstate_t **state,
229                           const char *cred_kind,
230                           const char *realmstring,
231                           svn_auth_baton_t *auth_baton,
232                           apr_pool_t *pool)
233{
234  int i = 0;
235  provider_set_t *table;
236  svn_auth_provider_object_t *provider = NULL;
237  void *creds = NULL;
238  void *iter_baton = NULL;
239  svn_boolean_t got_first = FALSE;
240  svn_auth_iterstate_t *iterstate;
241  const char *cache_key;
242  apr_hash_t *parameters;
243
244  if (! auth_baton)
245    return svn_error_create(SVN_ERR_AUTHN_NO_PROVIDER, NULL,
246                            _("No authentication providers registered"));
247
248  /* Get the appropriate table of providers for CRED_KIND. */
249  table = svn_hash_gets(auth_baton->tables, cred_kind);
250  if (! table)
251    return svn_error_createf(SVN_ERR_AUTHN_NO_PROVIDER, NULL,
252                             _("No provider registered for '%s' credentials"),
253                             cred_kind);
254
255  if (auth_baton->slave_parameters)
256    {
257      apr_hash_index_t *hi;
258      parameters = apr_hash_copy(pool, auth_baton->parameters);
259
260      for (hi = apr_hash_first(pool, auth_baton->slave_parameters);
261            hi;
262            hi = apr_hash_next(hi))
263        {
264          const void *value = apr_hash_this_val(hi);
265
266          if (value == &auth_NULL)
267            value = NULL;
268
269          svn_hash_sets(parameters, apr_hash_this_key(hi), value);
270        }
271    }
272  else
273    parameters = auth_baton->parameters;
274
275  /* First, see if we have cached creds in the auth_baton. */
276  cache_key = make_cache_key(cred_kind, realmstring, pool);
277  creds = svn_hash_gets(auth_baton->creds_cache, cache_key);
278  if (creds)
279    {
280       got_first = FALSE;
281    }
282  else
283    /* If not, find a provider that can give "first" credentials. */
284    {
285      /* Find a provider that can give "first" credentials. */
286      for (i = 0; i < table->providers->nelts; i++)
287        {
288          provider = APR_ARRAY_IDX(table->providers, i,
289                                   svn_auth_provider_object_t *);
290          SVN_ERR(provider->vtable->first_credentials(&creds, &iter_baton,
291                                                      provider->provider_baton,
292                                                      parameters,
293                                                      realmstring,
294                                                      auth_baton->pool));
295
296          if (creds != NULL)
297            {
298              got_first = TRUE;
299              break;
300            }
301        }
302    }
303
304  if (! creds)
305    {
306      *state = NULL;
307    }
308  else
309    {
310      /* Build an abstract iteration state. */
311      iterstate = apr_pcalloc(pool, sizeof(*iterstate));
312      iterstate->table = table;
313      iterstate->provider_idx = i;
314      iterstate->got_first = got_first;
315      iterstate->provider_iter_baton = iter_baton;
316      iterstate->realmstring = apr_pstrdup(pool, realmstring);
317      iterstate->cache_key = cache_key;
318      iterstate->auth_baton = auth_baton;
319      iterstate->parameters = parameters;
320      *state = iterstate;
321
322      /* Put the creds in the cache */
323      svn_hash_sets(auth_baton->creds_cache,
324                    apr_pstrdup(auth_baton->pool, cache_key),
325                    creds);
326    }
327
328  *credentials = creds;
329
330  return SVN_NO_ERROR;
331}
332
333
334svn_error_t *
335svn_auth_next_credentials(void **credentials,
336                          svn_auth_iterstate_t *state,
337                          apr_pool_t *pool)
338{
339  svn_auth_baton_t *auth_baton = state->auth_baton;
340  svn_auth_provider_object_t *provider;
341  provider_set_t *table = state->table;
342  void *creds = NULL;
343
344  /* Continue traversing the table from where we left off. */
345  for (/* no init */;
346       state->provider_idx < table->providers->nelts;
347       state->provider_idx++)
348    {
349      provider = APR_ARRAY_IDX(table->providers,
350                               state->provider_idx,
351                               svn_auth_provider_object_t *);
352      if (! state->got_first)
353        {
354          SVN_ERR(provider->vtable->first_credentials(
355                      &creds, &(state->provider_iter_baton),
356                      provider->provider_baton, state->parameters,
357                      state->realmstring, auth_baton->pool));
358          state->got_first = TRUE;
359        }
360      else if (provider->vtable->next_credentials)
361        {
362          SVN_ERR(provider->vtable->next_credentials(&creds,
363                                                     state->provider_iter_baton,
364                                                     provider->provider_baton,
365                                                     state->parameters,
366                                                     state->realmstring,
367                                                     auth_baton->pool));
368        }
369
370      if (creds != NULL)
371        {
372          /* Put the creds in the cache */
373          svn_hash_sets(auth_baton->creds_cache,
374                        apr_pstrdup(auth_baton->pool, state->cache_key),
375                        creds);
376          break;
377        }
378
379      state->got_first = FALSE;
380    }
381
382  *credentials = creds;
383
384  return SVN_NO_ERROR;
385}
386
387
388svn_error_t *
389svn_auth_save_credentials(svn_auth_iterstate_t *state,
390                          apr_pool_t *pool)
391{
392  int i;
393  svn_auth_provider_object_t *provider;
394  svn_boolean_t save_succeeded = FALSE;
395  const char *no_auth_cache;
396  void *creds;
397
398  if (! state || state->table->providers->nelts <= state->provider_idx)
399    return SVN_NO_ERROR;
400
401  creds = svn_hash_gets(state->auth_baton->creds_cache, state->cache_key);
402  if (! creds)
403    return SVN_NO_ERROR;
404
405  /* Do not save the creds if SVN_AUTH_PARAM_NO_AUTH_CACHE is set */
406  no_auth_cache = svn_hash_gets(state->parameters,
407                                SVN_AUTH_PARAM_NO_AUTH_CACHE);
408  if (no_auth_cache)
409    return SVN_NO_ERROR;
410
411  /* First, try to save the creds using the provider that produced them. */
412  provider = APR_ARRAY_IDX(state->table->providers,
413                           state->provider_idx,
414                           svn_auth_provider_object_t *);
415  if (provider->vtable->save_credentials)
416    SVN_ERR(provider->vtable->save_credentials(&save_succeeded,
417                                               creds,
418                                               provider->provider_baton,
419                                               state->parameters,
420                                               state->realmstring,
421                                               pool));
422  if (save_succeeded)
423    return SVN_NO_ERROR;
424
425  /* Otherwise, loop from the top of the list, asking every provider
426     to attempt a save.  ### todo: someday optimize so we don't
427     necessarily start from the top of the list. */
428  for (i = 0; i < state->table->providers->nelts; i++)
429    {
430      provider = APR_ARRAY_IDX(state->table->providers, i,
431                               svn_auth_provider_object_t *);
432      if (provider->vtable->save_credentials)
433        SVN_ERR(provider->vtable->save_credentials(&save_succeeded, creds,
434                                                   provider->provider_baton,
435                                                   state->parameters,
436                                                   state->realmstring,
437                                                   pool));
438
439      if (save_succeeded)
440        break;
441    }
442
443  /* ### notice that at the moment, if no provider can save, there's
444     no way the caller will know. */
445
446  return SVN_NO_ERROR;
447}
448
449
450svn_error_t *
451svn_auth_forget_credentials(svn_auth_baton_t *auth_baton,
452                            const char *cred_kind,
453                            const char *realmstring,
454                            apr_pool_t *scratch_pool)
455{
456  SVN_ERR_ASSERT((cred_kind && realmstring) || (!cred_kind && !realmstring));
457
458  /* If we have a CRED_KIND and REALMSTRING, we clear out just the
459     cached item (if any).  Otherwise, empty the whole hash. */
460  if (cred_kind)
461    {
462      svn_hash_sets(auth_baton->creds_cache,
463                    make_cache_key(cred_kind, realmstring, scratch_pool),
464                    NULL);
465    }
466  else
467    {
468      apr_hash_clear(auth_baton->creds_cache);
469    }
470
471  return SVN_NO_ERROR;
472}
473
474
475svn_auth_ssl_server_cert_info_t *
476svn_auth_ssl_server_cert_info_dup
477  (const svn_auth_ssl_server_cert_info_t *info, apr_pool_t *pool)
478{
479  svn_auth_ssl_server_cert_info_t *new_info
480    = apr_palloc(pool, sizeof(*new_info));
481
482  *new_info = *info;
483
484  new_info->hostname = apr_pstrdup(pool, new_info->hostname);
485  new_info->fingerprint = apr_pstrdup(pool, new_info->fingerprint);
486  new_info->valid_from = apr_pstrdup(pool, new_info->valid_from);
487  new_info->valid_until = apr_pstrdup(pool, new_info->valid_until);
488  new_info->issuer_dname = apr_pstrdup(pool, new_info->issuer_dname);
489  new_info->ascii_cert = apr_pstrdup(pool, new_info->ascii_cert);
490
491  return new_info;
492}
493
494svn_error_t *
495svn_auth_get_platform_specific_provider(svn_auth_provider_object_t **provider,
496                                        const char *provider_name,
497                                        const char *provider_type,
498                                        apr_pool_t *pool)
499{
500  *provider = NULL;
501
502  if (apr_strnatcmp(provider_name, "gnome_keyring") == 0 ||
503      apr_strnatcmp(provider_name, "kwallet") == 0)
504    {
505#if defined(SVN_HAVE_GNOME_KEYRING) || defined(SVN_HAVE_KWALLET) || defined (SVN_HAVE_LIBSECRET)
506      apr_dso_handle_t *dso;
507      apr_dso_handle_sym_t provider_function_symbol, version_function_symbol;
508      const char *library_label, *library_name;
509      const char *provider_function_name, *version_function_name;
510      library_name = apr_psprintf(pool,
511                                  "libsvn_auth_%s-%d.so.%d",
512                                  provider_name,
513                                  SVN_VER_MAJOR, SVN_SOVERSION);
514      library_label = apr_psprintf(pool, "svn_%s", provider_name);
515      provider_function_name = apr_psprintf(pool,
516                                            "svn_auth_get_%s_%s_provider",
517                                            provider_name, provider_type);
518      version_function_name = apr_psprintf(pool,
519                                           "svn_auth_%s_version",
520                                           provider_name);
521      SVN_ERR(svn_dso_load(&dso, library_name));
522      if (dso)
523        {
524          if (apr_dso_sym(&version_function_symbol,
525                          dso,
526                          version_function_name) == 0)
527            {
528              svn_version_func_t version_function
529                = version_function_symbol;
530              svn_version_checklist_t check_list[2];
531
532              check_list[0].label = library_label;
533              check_list[0].version_query = version_function;
534              check_list[1].label = NULL;
535              check_list[1].version_query = NULL;
536              SVN_ERR(svn_ver_check_list2(svn_subr_version(), check_list,
537                                          svn_ver_equal));
538            }
539          if (apr_dso_sym(&provider_function_symbol,
540                          dso,
541                          provider_function_name) == 0)
542            {
543              if (strcmp(provider_type, "simple") == 0)
544                {
545                  svn_auth_simple_provider_func_t provider_function
546                    = provider_function_symbol;
547                  provider_function(provider, pool);
548                }
549              else if (strcmp(provider_type, "ssl_client_cert_pw") == 0)
550                {
551                  svn_auth_ssl_client_cert_pw_provider_func_t provider_function
552                    = provider_function_symbol;
553                  provider_function(provider, pool);
554                }
555            }
556        }
557#endif
558    }
559  else
560    {
561#if defined(SVN_HAVE_GPG_AGENT)
562      if (strcmp(provider_name, "gpg_agent") == 0 &&
563          strcmp(provider_type, "simple") == 0)
564        {
565          svn_auth__get_gpg_agent_simple_provider(provider, pool);
566        }
567#endif
568#ifdef SVN_HAVE_KEYCHAIN_SERVICES
569      if (strcmp(provider_name, "keychain") == 0 &&
570          strcmp(provider_type, "simple") == 0)
571        {
572          svn_auth__get_keychain_simple_provider(provider, pool);
573        }
574      else if (strcmp(provider_name, "keychain") == 0 &&
575               strcmp(provider_type, "ssl_client_cert_pw") == 0)
576        {
577          svn_auth__get_keychain_ssl_client_cert_pw_provider(provider, pool);
578        }
579#endif
580
581#if defined(WIN32) && !defined(__MINGW32__)
582      if (strcmp(provider_name, "windows") == 0 &&
583          strcmp(provider_type, "simple") == 0)
584        {
585          svn_auth__get_windows_simple_provider(provider, pool);
586        }
587      else if (strcmp(provider_name, "windows") == 0 &&
588               strcmp(provider_type, "ssl_client_cert_pw") == 0)
589        {
590          svn_auth__get_windows_ssl_client_cert_pw_provider(provider, pool);
591        }
592      else if (strcmp(provider_name, "windows") == 0 &&
593               strcmp(provider_type, "ssl_server_trust") == 0)
594        {
595          svn_auth__get_windows_ssl_server_trust_provider(provider, pool);
596        }
597      else if (strcmp(provider_name, "windows") == 0 &&
598               strcmp(provider_type, "ssl_server_authority") == 0)
599        {
600          svn_auth__get_windows_ssl_server_authority_provider(provider, pool);
601        }
602#endif
603    }
604
605  return SVN_NO_ERROR;
606}
607
608svn_error_t *
609svn_auth_get_platform_specific_client_providers(apr_array_header_t **providers,
610                                                svn_config_t *config,
611                                                apr_pool_t *pool)
612{
613  svn_auth_provider_object_t *provider;
614  const char *password_stores_config_option;
615  apr_array_header_t *password_stores;
616  int i;
617
618#define SVN__MAYBE_ADD_PROVIDER(list, p) \
619  { if (p) APR_ARRAY_PUSH(list, svn_auth_provider_object_t *) = p; }
620
621#define SVN__DEFAULT_AUTH_PROVIDER_LIST \
622         "gnome-keyring,kwallet,keychain,gpg-agent,windows-cryptoapi"
623
624  *providers = apr_array_make(pool, 12, sizeof(svn_auth_provider_object_t *));
625
626  /* Fetch the configured list of password stores, and split them into
627     an array. */
628  svn_config_get(config,
629                 &password_stores_config_option,
630                 SVN_CONFIG_SECTION_AUTH,
631                 SVN_CONFIG_OPTION_PASSWORD_STORES,
632                 SVN__DEFAULT_AUTH_PROVIDER_LIST);
633  password_stores = svn_cstring_split(password_stores_config_option,
634                                      " ,", TRUE, pool);
635
636  for (i = 0; i < password_stores->nelts; i++)
637    {
638      const char *password_store = APR_ARRAY_IDX(password_stores, i,
639                                                 const char *);
640
641      /* GNOME Keyring */
642      if (apr_strnatcmp(password_store, "gnome-keyring") == 0)
643        {
644          SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
645                                                          "gnome_keyring",
646                                                          "simple",
647                                                          pool));
648          SVN__MAYBE_ADD_PROVIDER(*providers, provider);
649
650          SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
651                                                          "gnome_keyring",
652                                                          "ssl_client_cert_pw",
653                                                          pool));
654          SVN__MAYBE_ADD_PROVIDER(*providers, provider);
655        }
656      /* GPG-AGENT */
657      else if (apr_strnatcmp(password_store, "gpg-agent") == 0)
658        {
659          SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
660                                                          "gpg_agent",
661                                                          "simple",
662                                                          pool));
663          SVN__MAYBE_ADD_PROVIDER(*providers, provider);
664        }
665      /* KWallet */
666      else if (apr_strnatcmp(password_store, "kwallet") == 0)
667        {
668          SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
669                                                          "kwallet",
670                                                          "simple",
671                                                          pool));
672          SVN__MAYBE_ADD_PROVIDER(*providers, provider);
673
674          SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
675                                                          "kwallet",
676                                                          "ssl_client_cert_pw",
677                                                          pool));
678          SVN__MAYBE_ADD_PROVIDER(*providers, provider);
679        }
680      /* Keychain */
681      else if (apr_strnatcmp(password_store, "keychain") == 0)
682        {
683          SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
684                                                          "keychain",
685                                                          "simple",
686                                                          pool));
687          SVN__MAYBE_ADD_PROVIDER(*providers, provider);
688
689          SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
690                                                          "keychain",
691                                                          "ssl_client_cert_pw",
692                                                          pool));
693          SVN__MAYBE_ADD_PROVIDER(*providers, provider);
694        }
695      /* Windows */
696      else if (apr_strnatcmp(password_store, "windows-cryptoapi") == 0)
697        {
698          SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
699                                                          "windows",
700                                                          "simple",
701                                                          pool));
702          SVN__MAYBE_ADD_PROVIDER(*providers, provider);
703
704          SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
705                                                          "windows",
706                                                          "ssl_client_cert_pw",
707                                                          pool));
708          SVN__MAYBE_ADD_PROVIDER(*providers, provider);
709        }
710    }
711
712  /* Windows has two providers without a store to allow easy access to
713     SSL servers. We enable these unconditionally.
714     (This behavior was moved here from svn_cmdline_create_auth_baton()) */
715  SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
716                                                  "windows",
717                                                  "ssl_server_trust",
718                                                  pool));
719  SVN__MAYBE_ADD_PROVIDER(*providers, provider);
720
721  /* The windows ssl authority certificate CRYPTOAPI provider. */
722  SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
723                                                  "windows",
724                                                  "ssl_server_authority",
725                                                  pool));
726
727  SVN__MAYBE_ADD_PROVIDER(*providers, provider);
728
729  return SVN_NO_ERROR;
730}
731
732svn_error_t *
733svn_auth__make_session_auth(svn_auth_baton_t **session_auth_baton,
734                            const svn_auth_baton_t *auth_baton,
735                            apr_hash_t *config,
736                            const char *server_name,
737                            apr_pool_t *result_pool,
738                            apr_pool_t *scratch_pool)
739{
740  svn_boolean_t store_passwords = SVN_CONFIG_DEFAULT_OPTION_STORE_PASSWORDS;
741  svn_boolean_t store_auth_creds = SVN_CONFIG_DEFAULT_OPTION_STORE_AUTH_CREDS;
742  const char *store_plaintext_passwords
743    = SVN_CONFIG_DEFAULT_OPTION_STORE_PLAINTEXT_PASSWORDS;
744  svn_boolean_t store_pp = SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP;
745  const char *store_pp_plaintext
746    = SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT;
747  svn_config_t *servers = NULL;
748  const char *server_group = NULL;
749
750  struct svn_auth_baton_t *ab;
751
752  ab = apr_pmemdup(result_pool, auth_baton, sizeof(*ab));
753
754  ab->slave_parameters = apr_hash_make(result_pool);
755
756  /* The 'store-passwords' and 'store-auth-creds' parameters used to
757  * live in SVN_CONFIG_CATEGORY_CONFIG. For backward compatibility,
758  * if values for these parameters have already been set by our
759  * callers, we use those values as defaults.
760  *
761  * Note that we can only catch the case where users explicitly set
762  * "store-passwords = no" or 'store-auth-creds = no".
763  *
764  * However, since the default value for both these options is
765  * currently (and has always been) "yes", users won't know
766  * the difference if they set "store-passwords = yes" or
767  * "store-auth-creds = yes" -- they'll get the expected behaviour.
768  */
769
770  if (svn_auth_get_parameter(ab, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS) != NULL)
771    store_passwords = FALSE;
772
773  if (svn_auth_get_parameter(ab, SVN_AUTH_PARAM_NO_AUTH_CACHE) != NULL)
774    store_auth_creds = FALSE;
775
776  /* All the svn_auth_set_parameter() calls below this not only affect the
777     to be created ra session, but also all the ra sessions that are already
778     use this auth baton!
779
780     Please try to key things based on the realm string instead of this
781     construct.
782 */
783
784  if (config)
785    {
786      /* Grab the 'servers' config. */
787      servers = svn_hash_gets(config, SVN_CONFIG_CATEGORY_SERVERS);
788      if (servers)
789        {
790          /* First, look in the global section. */
791
792          SVN_ERR(svn_config_get_bool
793            (servers, &store_passwords, SVN_CONFIG_SECTION_GLOBAL,
794             SVN_CONFIG_OPTION_STORE_PASSWORDS,
795             store_passwords));
796
797          SVN_ERR(svn_config_get_yes_no_ask
798            (servers, &store_plaintext_passwords, SVN_CONFIG_SECTION_GLOBAL,
799             SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS,
800             SVN_CONFIG_DEFAULT_OPTION_STORE_PLAINTEXT_PASSWORDS));
801
802          SVN_ERR(svn_config_get_bool
803            (servers, &store_pp, SVN_CONFIG_SECTION_GLOBAL,
804             SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP,
805             store_pp));
806
807          SVN_ERR(svn_config_get_yes_no_ask
808            (servers, &store_pp_plaintext,
809             SVN_CONFIG_SECTION_GLOBAL,
810             SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT,
811             SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT));
812
813          SVN_ERR(svn_config_get_bool
814            (servers, &store_auth_creds, SVN_CONFIG_SECTION_GLOBAL,
815              SVN_CONFIG_OPTION_STORE_AUTH_CREDS,
816              store_auth_creds));
817
818          /* Find out where we're about to connect to, and
819           * try to pick a server group based on the destination. */
820          server_group = svn_config_find_group(servers, server_name,
821                                               SVN_CONFIG_SECTION_GROUPS,
822                                               scratch_pool);
823
824          if (server_group)
825            {
826              /* Override global auth caching parameters with the ones
827               * for the server group, if any. */
828              SVN_ERR(svn_config_get_bool(servers, &store_auth_creds,
829                                          server_group,
830                                          SVN_CONFIG_OPTION_STORE_AUTH_CREDS,
831                                          store_auth_creds));
832
833              SVN_ERR(svn_config_get_bool(servers, &store_passwords,
834                                          server_group,
835                                          SVN_CONFIG_OPTION_STORE_PASSWORDS,
836                                          store_passwords));
837
838              SVN_ERR(svn_config_get_yes_no_ask
839                (servers, &store_plaintext_passwords, server_group,
840                 SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS,
841                 store_plaintext_passwords));
842
843              SVN_ERR(svn_config_get_bool
844                (servers, &store_pp,
845                 server_group, SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP,
846                 store_pp));
847
848              SVN_ERR(svn_config_get_yes_no_ask
849                (servers, &store_pp_plaintext, server_group,
850                 SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT,
851                 store_pp_plaintext));
852            }
853        }
854    }
855
856  /* Save auth caching parameters in the auth parameter hash. */
857  if (! store_passwords)
858    svn_auth_set_parameter(ab, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, "");
859
860  svn_auth_set_parameter(ab,
861                         SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS,
862                         store_plaintext_passwords);
863
864  if (! store_pp)
865    svn_auth_set_parameter(ab,
866                           SVN_AUTH_PARAM_DONT_STORE_SSL_CLIENT_CERT_PP,
867                           "");
868
869  svn_auth_set_parameter(ab,
870                         SVN_AUTH_PARAM_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT,
871                         store_pp_plaintext);
872
873  if (! store_auth_creds)
874    svn_auth_set_parameter(ab, SVN_AUTH_PARAM_NO_AUTH_CACHE, "");
875
876  if (server_group)
877    svn_auth_set_parameter(ab,
878                           SVN_AUTH_PARAM_SERVER_GROUP,
879                           apr_pstrdup(ab->pool, server_group));
880
881  *session_auth_baton = ab;
882
883  return SVN_NO_ERROR;
884}
885
886
887static svn_error_t *
888dummy_first_creds(void **credentials,
889                  void **iter_baton,
890                  void *provider_baton,
891                  apr_hash_t *parameters,
892                  const char *realmstring,
893                  apr_pool_t *pool)
894{
895  *credentials = NULL;
896  *iter_baton = NULL;
897  return SVN_NO_ERROR;
898}
899
900void
901svn_auth__get_dummmy_simple_provider(svn_auth_provider_object_t **provider,
902                                     apr_pool_t *pool)
903{
904  static const svn_auth_provider_t vtable = {
905    SVN_AUTH_CRED_SIMPLE,
906    dummy_first_creds,
907    NULL, NULL
908  };
909
910  svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
911
912  po->vtable = &vtable;
913  *provider = po;
914}
915