1251881Speter/*
2251881Speter * auth.c: authentication support functions for Subversion
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#include <apr_pools.h>
26251881Speter#include <apr_tables.h>
27251881Speter#include <apr_strings.h>
28251881Speter
29251881Speter#include "svn_hash.h"
30251881Speter#include "svn_types.h"
31251881Speter#include "svn_string.h"
32251881Speter#include "svn_error.h"
33251881Speter#include "svn_auth.h"
34251881Speter#include "svn_config.h"
35251881Speter#include "svn_private_config.h"
36251881Speter#include "svn_dso.h"
37251881Speter#include "svn_version.h"
38251881Speter#include "private/svn_dep_compat.h"
39251881Speter
40251881Speter#include "auth.h"
41251881Speter
42251881Speter/* AN OVERVIEW
43251881Speter   ===========
44251881Speter
45251881Speter   A good way to think of this machinery is as a set of tables.
46251881Speter
47251881Speter     - Each type of credentials selects a single table.
48251881Speter
49251881Speter     - In a given table, each row is a 'provider' capable of returning
50251881Speter       the same type of credentials.  Each column represents a
51251881Speter       provider's repeated attempts to provide credentials.
52251881Speter
53251881Speter
54251881Speter   Fetching Credentials from Providers
55251881Speter   -----------------------------------
56251881Speter
57251881Speter   When the caller asks for a particular type of credentials, the
58251881Speter   machinery in this file walks over the appropriate table.  It starts
59251881Speter   with the first provider (first row), and calls first_credentials()
60251881Speter   to get the first set of credentials (first column).  If the caller
61251881Speter   is unhappy with the credentials, then each subsequent call to
62251881Speter   next_credentials() traverses the row from left to right.  If the
63251881Speter   provider returns error at any point, then we go to the next provider
64251881Speter   (row).  We continue this way until every provider fails, or
65251881Speter   until the client is happy with the returned credentials.
66251881Speter
67251881Speter   Note that the caller cannot see the table traversal, and thus has
68251881Speter   no idea when we switch providers.
69251881Speter
70251881Speter
71251881Speter   Storing Credentials with Providers
72251881Speter   ----------------------------------
73251881Speter
74251881Speter   When the server has validated a set of credentials, and when
75251881Speter   credential caching is enabled, we have the chance to store those
76251881Speter   credentials for later use.  The provider which provided the working
77251881Speter   credentials is the first one given the opportunity to (re)cache
78251881Speter   those credentials.  Its save_credentials() function is invoked with
79251881Speter   the working credentials.  If that provider reports that it
80251881Speter   successfully stored the credentials, we're done.  Otherwise, we
81251881Speter   walk the providers (rows) for that type of credentials in order
82251881Speter   from the top of the table, allowing each in turn the opportunity to
83251881Speter   store the credentials.  When one reports that it has done so
84251881Speter   successfully -- or when we run out of providers (rows) to try --
85251881Speter   the table walk ends.
86251881Speter*/
87251881Speter
88251881Speter
89251881Speter
90251881Speter/* This effectively defines a single table.  Every provider in this
91251881Speter   array returns the same kind of credentials. */
92251881Spetertypedef struct provider_set_t
93251881Speter{
94251881Speter  /* ordered array of svn_auth_provider_object_t */
95251881Speter  apr_array_header_t *providers;
96251881Speter
97251881Speter} provider_set_t;
98251881Speter
99251881Speter
100251881Speter/* The main auth baton. */
101251881Speterstruct svn_auth_baton_t
102251881Speter{
103251881Speter  /* a collection of tables.  maps cred_kind -> provider_set */
104251881Speter  apr_hash_t *tables;
105251881Speter
106251881Speter  /* the pool I'm allocated in. */
107251881Speter  apr_pool_t *pool;
108251881Speter
109251881Speter  /* run-time parameters needed by providers. */
110251881Speter  apr_hash_t *parameters;
111251881Speter
112251881Speter  /* run-time credentials cache. */
113251881Speter  apr_hash_t *creds_cache;
114251881Speter
115251881Speter};
116251881Speter
117251881Speter/* Abstracted iteration baton */
118251881Speterstruct svn_auth_iterstate_t
119251881Speter{
120251881Speter  provider_set_t *table;        /* the table being searched */
121251881Speter  int provider_idx;             /* the current provider (row) */
122251881Speter  svn_boolean_t got_first;      /* did we get the provider's first creds? */
123251881Speter  void *provider_iter_baton;    /* the provider's own iteration context */
124251881Speter  const char *realmstring;      /* The original realmstring passed in */
125251881Speter  const char *cache_key;        /* key to use in auth_baton's creds_cache */
126251881Speter  svn_auth_baton_t *auth_baton; /* the original auth_baton. */
127251881Speter};
128251881Speter
129251881Speter
130251881Speter
131251881Spetervoid
132251881Spetersvn_auth_open(svn_auth_baton_t **auth_baton,
133251881Speter              const apr_array_header_t *providers,
134251881Speter              apr_pool_t *pool)
135251881Speter{
136251881Speter  svn_auth_baton_t *ab;
137251881Speter  svn_auth_provider_object_t *provider;
138251881Speter  int i;
139251881Speter
140251881Speter  /* Build the auth_baton. */
141251881Speter  ab = apr_pcalloc(pool, sizeof(*ab));
142251881Speter  ab->tables = apr_hash_make(pool);
143251881Speter  ab->parameters = apr_hash_make(pool);
144251881Speter  ab->creds_cache = apr_hash_make(pool);
145251881Speter  ab->pool = pool;
146251881Speter
147251881Speter  /* Register each provider in order.  Providers of different
148251881Speter     credentials will be automatically sorted into different tables by
149251881Speter     register_provider(). */
150251881Speter  for (i = 0; i < providers->nelts; i++)
151251881Speter    {
152251881Speter      provider_set_t *table;
153251881Speter      provider = APR_ARRAY_IDX(providers, i, svn_auth_provider_object_t *);
154251881Speter
155251881Speter      /* Add it to the appropriate table in the auth_baton */
156251881Speter      table = svn_hash_gets(ab->tables, provider->vtable->cred_kind);
157251881Speter      if (! table)
158251881Speter        {
159251881Speter          table = apr_pcalloc(pool, sizeof(*table));
160251881Speter          table->providers
161251881Speter            = apr_array_make(pool, 1, sizeof(svn_auth_provider_object_t *));
162251881Speter
163251881Speter          svn_hash_sets(ab->tables, provider->vtable->cred_kind, table);
164251881Speter        }
165251881Speter      APR_ARRAY_PUSH(table->providers, svn_auth_provider_object_t *)
166251881Speter        = provider;
167251881Speter    }
168251881Speter
169251881Speter  *auth_baton = ab;
170251881Speter}
171251881Speter
172251881Speter
173251881Speter
174251881Spetervoid
175251881Spetersvn_auth_set_parameter(svn_auth_baton_t *auth_baton,
176251881Speter                       const char *name,
177251881Speter                       const void *value)
178251881Speter{
179251881Speter  svn_hash_sets(auth_baton->parameters, name, value);
180251881Speter}
181251881Speter
182251881Speterconst void *
183251881Spetersvn_auth_get_parameter(svn_auth_baton_t *auth_baton,
184251881Speter                       const char *name)
185251881Speter{
186251881Speter  return svn_hash_gets(auth_baton->parameters, name);
187251881Speter}
188251881Speter
189251881Speter
190251881Speter/* Return the key used to address the in-memory cache of auth
191251881Speter   credentials of type CRED_KIND and associated with REALMSTRING. */
192251881Speterstatic const char *
193251881Spetermake_cache_key(const char *cred_kind,
194251881Speter               const char *realmstring,
195251881Speter               apr_pool_t *pool)
196251881Speter{
197251881Speter  return apr_pstrcat(pool, cred_kind, ":", realmstring, (char *)NULL);
198251881Speter}
199251881Speter
200251881Spetersvn_error_t *
201251881Spetersvn_auth_first_credentials(void **credentials,
202251881Speter                           svn_auth_iterstate_t **state,
203251881Speter                           const char *cred_kind,
204251881Speter                           const char *realmstring,
205251881Speter                           svn_auth_baton_t *auth_baton,
206251881Speter                           apr_pool_t *pool)
207251881Speter{
208251881Speter  int i = 0;
209251881Speter  provider_set_t *table;
210251881Speter  svn_auth_provider_object_t *provider = NULL;
211251881Speter  void *creds = NULL;
212251881Speter  void *iter_baton = NULL;
213251881Speter  svn_boolean_t got_first = FALSE;
214251881Speter  svn_auth_iterstate_t *iterstate;
215251881Speter  const char *cache_key;
216251881Speter
217251881Speter  /* Get the appropriate table of providers for CRED_KIND. */
218251881Speter  table = svn_hash_gets(auth_baton->tables, cred_kind);
219251881Speter  if (! table)
220251881Speter    return svn_error_createf(SVN_ERR_AUTHN_NO_PROVIDER, NULL,
221251881Speter                             _("No provider registered for '%s' credentials"),
222251881Speter                             cred_kind);
223251881Speter
224251881Speter  /* First, see if we have cached creds in the auth_baton. */
225251881Speter  cache_key = make_cache_key(cred_kind, realmstring, pool);
226251881Speter  creds = svn_hash_gets(auth_baton->creds_cache, cache_key);
227251881Speter  if (creds)
228251881Speter    {
229251881Speter       got_first = FALSE;
230251881Speter    }
231251881Speter  else
232251881Speter    /* If not, find a provider that can give "first" credentials. */
233251881Speter    {
234251881Speter      /* Find a provider that can give "first" credentials. */
235251881Speter      for (i = 0; i < table->providers->nelts; i++)
236251881Speter        {
237251881Speter          provider = APR_ARRAY_IDX(table->providers, i,
238251881Speter                                   svn_auth_provider_object_t *);
239251881Speter          SVN_ERR(provider->vtable->first_credentials(&creds, &iter_baton,
240251881Speter                                                      provider->provider_baton,
241251881Speter                                                      auth_baton->parameters,
242251881Speter                                                      realmstring,
243251881Speter                                                      auth_baton->pool));
244251881Speter
245251881Speter          if (creds != NULL)
246251881Speter            {
247251881Speter              got_first = TRUE;
248251881Speter              break;
249251881Speter            }
250251881Speter        }
251251881Speter    }
252251881Speter
253251881Speter  if (! creds)
254251881Speter    *state = NULL;
255251881Speter  else
256251881Speter    {
257251881Speter      /* Build an abstract iteration state. */
258251881Speter      iterstate = apr_pcalloc(pool, sizeof(*iterstate));
259251881Speter      iterstate->table = table;
260251881Speter      iterstate->provider_idx = i;
261251881Speter      iterstate->got_first = got_first;
262251881Speter      iterstate->provider_iter_baton = iter_baton;
263251881Speter      iterstate->realmstring = apr_pstrdup(pool, realmstring);
264251881Speter      iterstate->cache_key = cache_key;
265251881Speter      iterstate->auth_baton = auth_baton;
266251881Speter      *state = iterstate;
267251881Speter
268251881Speter      /* Put the creds in the cache */
269251881Speter      svn_hash_sets(auth_baton->creds_cache,
270251881Speter                    apr_pstrdup(auth_baton->pool, cache_key),
271251881Speter                    creds);
272251881Speter    }
273251881Speter
274251881Speter  *credentials = creds;
275251881Speter
276251881Speter  return SVN_NO_ERROR;
277251881Speter}
278251881Speter
279251881Speter
280251881Spetersvn_error_t *
281251881Spetersvn_auth_next_credentials(void **credentials,
282251881Speter                          svn_auth_iterstate_t *state,
283251881Speter                          apr_pool_t *pool)
284251881Speter{
285251881Speter  svn_auth_baton_t *auth_baton = state->auth_baton;
286251881Speter  svn_auth_provider_object_t *provider;
287251881Speter  provider_set_t *table = state->table;
288251881Speter  void *creds = NULL;
289251881Speter
290251881Speter  /* Continue traversing the table from where we left off. */
291251881Speter  for (/* no init */;
292251881Speter       state->provider_idx < table->providers->nelts;
293251881Speter       state->provider_idx++)
294251881Speter    {
295251881Speter      provider = APR_ARRAY_IDX(table->providers,
296251881Speter                               state->provider_idx,
297251881Speter                               svn_auth_provider_object_t *);
298251881Speter      if (! state->got_first)
299251881Speter        {
300251881Speter          SVN_ERR(provider->vtable->first_credentials(
301251881Speter                      &creds, &(state->provider_iter_baton),
302251881Speter                      provider->provider_baton, auth_baton->parameters,
303251881Speter                      state->realmstring, auth_baton->pool));
304251881Speter          state->got_first = TRUE;
305251881Speter        }
306251881Speter      else if (provider->vtable->next_credentials)
307251881Speter        {
308251881Speter          SVN_ERR(provider->vtable->next_credentials(
309251881Speter                      &creds, state->provider_iter_baton,
310251881Speter                      provider->provider_baton, auth_baton->parameters,
311251881Speter                      state->realmstring, auth_baton->pool));
312251881Speter        }
313251881Speter
314251881Speter      if (creds != NULL)
315251881Speter        {
316251881Speter          /* Put the creds in the cache */
317251881Speter          svn_hash_sets(auth_baton->creds_cache, state->cache_key, creds);
318251881Speter          break;
319251881Speter        }
320251881Speter
321251881Speter      state->got_first = FALSE;
322251881Speter    }
323251881Speter
324251881Speter  *credentials = creds;
325251881Speter
326251881Speter  return SVN_NO_ERROR;
327251881Speter}
328251881Speter
329251881Speter
330251881Spetersvn_error_t *
331251881Spetersvn_auth_save_credentials(svn_auth_iterstate_t *state,
332251881Speter                          apr_pool_t *pool)
333251881Speter{
334251881Speter  int i;
335251881Speter  svn_auth_provider_object_t *provider;
336251881Speter  svn_boolean_t save_succeeded = FALSE;
337251881Speter  const char *no_auth_cache;
338251881Speter  svn_auth_baton_t *auth_baton;
339251881Speter  void *creds;
340251881Speter
341251881Speter  if (! state || state->table->providers->nelts <= state->provider_idx)
342251881Speter    return SVN_NO_ERROR;
343251881Speter
344251881Speter  auth_baton = state->auth_baton;
345251881Speter  creds = svn_hash_gets(state->auth_baton->creds_cache, state->cache_key);
346251881Speter  if (! creds)
347251881Speter    return SVN_NO_ERROR;
348251881Speter
349251881Speter  /* Do not save the creds if SVN_AUTH_PARAM_NO_AUTH_CACHE is set */
350251881Speter  no_auth_cache = svn_hash_gets(auth_baton->parameters,
351251881Speter                                SVN_AUTH_PARAM_NO_AUTH_CACHE);
352251881Speter  if (no_auth_cache)
353251881Speter    return SVN_NO_ERROR;
354251881Speter
355251881Speter  /* First, try to save the creds using the provider that produced them. */
356251881Speter  provider = APR_ARRAY_IDX(state->table->providers,
357251881Speter                           state->provider_idx,
358251881Speter                           svn_auth_provider_object_t *);
359251881Speter  if (provider->vtable->save_credentials)
360251881Speter    SVN_ERR(provider->vtable->save_credentials(&save_succeeded,
361251881Speter                                               creds,
362251881Speter                                               provider->provider_baton,
363251881Speter                                               auth_baton->parameters,
364251881Speter                                               state->realmstring,
365251881Speter                                               pool));
366251881Speter  if (save_succeeded)
367251881Speter    return SVN_NO_ERROR;
368251881Speter
369251881Speter  /* Otherwise, loop from the top of the list, asking every provider
370251881Speter     to attempt a save.  ### todo: someday optimize so we don't
371251881Speter     necessarily start from the top of the list. */
372251881Speter  for (i = 0; i < state->table->providers->nelts; i++)
373251881Speter    {
374251881Speter      provider = APR_ARRAY_IDX(state->table->providers, i,
375251881Speter                               svn_auth_provider_object_t *);
376251881Speter      if (provider->vtable->save_credentials)
377251881Speter        SVN_ERR(provider->vtable->save_credentials
378251881Speter                (&save_succeeded, creds,
379251881Speter                 provider->provider_baton,
380251881Speter                 auth_baton->parameters,
381251881Speter                 state->realmstring,
382251881Speter                 pool));
383251881Speter
384251881Speter      if (save_succeeded)
385251881Speter        break;
386251881Speter    }
387251881Speter
388251881Speter  /* ### notice that at the moment, if no provider can save, there's
389251881Speter     no way the caller will know. */
390251881Speter
391251881Speter  return SVN_NO_ERROR;
392251881Speter}
393251881Speter
394251881Speter
395251881Spetersvn_error_t *
396251881Spetersvn_auth_forget_credentials(svn_auth_baton_t *auth_baton,
397251881Speter                            const char *cred_kind,
398251881Speter                            const char *realmstring,
399251881Speter                            apr_pool_t *scratch_pool)
400251881Speter{
401251881Speter  SVN_ERR_ASSERT((cred_kind && realmstring) || (!cred_kind && !realmstring));
402251881Speter
403251881Speter  /* If we have a CRED_KIND and REALMSTRING, we clear out just the
404251881Speter     cached item (if any).  Otherwise, empty the whole hash. */
405251881Speter  if (cred_kind)
406251881Speter    {
407251881Speter      svn_hash_sets(auth_baton->creds_cache,
408251881Speter                    make_cache_key(cred_kind, realmstring, scratch_pool),
409251881Speter                    NULL);
410251881Speter    }
411251881Speter  else
412251881Speter    {
413251881Speter      apr_hash_clear(auth_baton->creds_cache);
414251881Speter    }
415251881Speter
416251881Speter  return SVN_NO_ERROR;
417251881Speter}
418251881Speter
419251881Speter
420251881Spetersvn_auth_ssl_server_cert_info_t *
421251881Spetersvn_auth_ssl_server_cert_info_dup
422251881Speter  (const svn_auth_ssl_server_cert_info_t *info, apr_pool_t *pool)
423251881Speter{
424251881Speter  svn_auth_ssl_server_cert_info_t *new_info
425251881Speter    = apr_palloc(pool, sizeof(*new_info));
426251881Speter
427251881Speter  *new_info = *info;
428251881Speter
429251881Speter  new_info->hostname = apr_pstrdup(pool, new_info->hostname);
430251881Speter  new_info->fingerprint = apr_pstrdup(pool, new_info->fingerprint);
431251881Speter  new_info->valid_from = apr_pstrdup(pool, new_info->valid_from);
432251881Speter  new_info->valid_until = apr_pstrdup(pool, new_info->valid_until);
433251881Speter  new_info->issuer_dname = apr_pstrdup(pool, new_info->issuer_dname);
434251881Speter  new_info->ascii_cert = apr_pstrdup(pool, new_info->ascii_cert);
435251881Speter
436251881Speter  return new_info;
437251881Speter}
438251881Speter
439251881Spetersvn_error_t *
440251881Spetersvn_auth_get_platform_specific_provider(svn_auth_provider_object_t **provider,
441251881Speter                                        const char *provider_name,
442251881Speter                                        const char *provider_type,
443251881Speter                                        apr_pool_t *pool)
444251881Speter{
445251881Speter  *provider = NULL;
446251881Speter
447251881Speter  if (apr_strnatcmp(provider_name, "gnome_keyring") == 0 ||
448251881Speter      apr_strnatcmp(provider_name, "kwallet") == 0)
449251881Speter    {
450251881Speter#if defined(SVN_HAVE_GNOME_KEYRING) || defined(SVN_HAVE_KWALLET)
451251881Speter      apr_dso_handle_t *dso;
452251881Speter      apr_dso_handle_sym_t provider_function_symbol, version_function_symbol;
453251881Speter      const char *library_label, *library_name;
454251881Speter      const char *provider_function_name, *version_function_name;
455251881Speter      library_name = apr_psprintf(pool,
456251881Speter                                  "libsvn_auth_%s-%d.so.%d",
457251881Speter                                  provider_name,
458251881Speter                                  SVN_VER_MAJOR, SVN_SOVERSION);
459251881Speter      library_label = apr_psprintf(pool, "svn_%s", provider_name);
460251881Speter      provider_function_name = apr_psprintf(pool,
461251881Speter                                            "svn_auth_get_%s_%s_provider",
462251881Speter                                            provider_name, provider_type);
463251881Speter      version_function_name = apr_psprintf(pool,
464251881Speter                                           "svn_auth_%s_version",
465251881Speter                                           provider_name);
466251881Speter      SVN_ERR(svn_dso_load(&dso, library_name));
467251881Speter      if (dso)
468251881Speter        {
469251881Speter          if (apr_dso_sym(&version_function_symbol,
470251881Speter                          dso,
471251881Speter                          version_function_name) == 0)
472251881Speter            {
473251881Speter              svn_version_func_t version_function
474251881Speter                = version_function_symbol;
475251881Speter              svn_version_checklist_t check_list[2];
476251881Speter
477251881Speter              check_list[0].label = library_label;
478251881Speter              check_list[0].version_query = version_function;
479251881Speter              check_list[1].label = NULL;
480251881Speter              check_list[1].version_query = NULL;
481251881Speter              SVN_ERR(svn_ver_check_list(svn_subr_version(), check_list));
482251881Speter            }
483251881Speter          if (apr_dso_sym(&provider_function_symbol,
484251881Speter                          dso,
485251881Speter                          provider_function_name) == 0)
486251881Speter            {
487251881Speter              if (strcmp(provider_type, "simple") == 0)
488251881Speter                {
489251881Speter                  svn_auth_simple_provider_func_t provider_function
490251881Speter                    = provider_function_symbol;
491251881Speter                  provider_function(provider, pool);
492251881Speter                }
493251881Speter              else if (strcmp(provider_type, "ssl_client_cert_pw") == 0)
494251881Speter                {
495251881Speter                  svn_auth_ssl_client_cert_pw_provider_func_t provider_function
496251881Speter                    = provider_function_symbol;
497251881Speter                  provider_function(provider, pool);
498251881Speter                }
499251881Speter            }
500251881Speter        }
501251881Speter#endif
502251881Speter    }
503251881Speter  else
504251881Speter    {
505251881Speter#if defined(SVN_HAVE_GPG_AGENT)
506251881Speter      if (strcmp(provider_name, "gpg_agent") == 0 &&
507251881Speter          strcmp(provider_type, "simple") == 0)
508251881Speter        {
509251881Speter          svn_auth_get_gpg_agent_simple_provider(provider, pool);
510251881Speter        }
511251881Speter#endif
512251881Speter#ifdef SVN_HAVE_KEYCHAIN_SERVICES
513251881Speter      if (strcmp(provider_name, "keychain") == 0 &&
514251881Speter          strcmp(provider_type, "simple") == 0)
515251881Speter        {
516251881Speter          svn_auth_get_keychain_simple_provider(provider, pool);
517251881Speter        }
518251881Speter      else if (strcmp(provider_name, "keychain") == 0 &&
519251881Speter               strcmp(provider_type, "ssl_client_cert_pw") == 0)
520251881Speter        {
521251881Speter          svn_auth_get_keychain_ssl_client_cert_pw_provider(provider, pool);
522251881Speter        }
523251881Speter#endif
524251881Speter
525251881Speter#if defined(WIN32) && !defined(__MINGW32__)
526251881Speter      if (strcmp(provider_name, "windows") == 0 &&
527251881Speter          strcmp(provider_type, "simple") == 0)
528251881Speter        {
529251881Speter          svn_auth_get_windows_simple_provider(provider, pool);
530251881Speter        }
531251881Speter      else if (strcmp(provider_name, "windows") == 0 &&
532251881Speter               strcmp(provider_type, "ssl_client_cert_pw") == 0)
533251881Speter        {
534251881Speter          svn_auth_get_windows_ssl_client_cert_pw_provider(provider, pool);
535251881Speter        }
536251881Speter      else if (strcmp(provider_name, "windows") == 0 &&
537251881Speter               strcmp(provider_type, "ssl_server_trust") == 0)
538251881Speter        {
539251881Speter          svn_auth_get_windows_ssl_server_trust_provider(provider, pool);
540251881Speter        }
541251881Speter#endif
542251881Speter    }
543251881Speter
544251881Speter  return SVN_NO_ERROR;
545251881Speter}
546251881Speter
547251881Spetersvn_error_t *
548251881Spetersvn_auth_get_platform_specific_client_providers(apr_array_header_t **providers,
549251881Speter                                                svn_config_t *config,
550251881Speter                                                apr_pool_t *pool)
551251881Speter{
552251881Speter  svn_auth_provider_object_t *provider;
553251881Speter  const char *password_stores_config_option;
554251881Speter  apr_array_header_t *password_stores;
555251881Speter  int i;
556251881Speter
557251881Speter#define SVN__MAYBE_ADD_PROVIDER(list, p) \
558251881Speter  { if (p) APR_ARRAY_PUSH(list, svn_auth_provider_object_t *) = p; }
559251881Speter
560251881Speter#define SVN__DEFAULT_AUTH_PROVIDER_LIST \
561251881Speter         "gnome-keyring,kwallet,keychain,gpg-agent,windows-cryptoapi"
562251881Speter
563251881Speter  *providers = apr_array_make(pool, 12, sizeof(svn_auth_provider_object_t *));
564251881Speter
565251881Speter  /* Fetch the configured list of password stores, and split them into
566251881Speter     an array. */
567251881Speter  svn_config_get(config,
568251881Speter                 &password_stores_config_option,
569251881Speter                 SVN_CONFIG_SECTION_AUTH,
570251881Speter                 SVN_CONFIG_OPTION_PASSWORD_STORES,
571251881Speter                 SVN__DEFAULT_AUTH_PROVIDER_LIST);
572251881Speter  password_stores = svn_cstring_split(password_stores_config_option,
573251881Speter                                      " ,", TRUE, pool);
574251881Speter
575251881Speter  for (i = 0; i < password_stores->nelts; i++)
576251881Speter    {
577251881Speter      const char *password_store = APR_ARRAY_IDX(password_stores, i,
578251881Speter                                                 const char *);
579251881Speter
580251881Speter      /* GNOME Keyring */
581251881Speter      if (apr_strnatcmp(password_store, "gnome-keyring") == 0)
582251881Speter        {
583251881Speter          SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
584251881Speter                                                          "gnome_keyring",
585251881Speter                                                          "simple",
586251881Speter                                                          pool));
587251881Speter          SVN__MAYBE_ADD_PROVIDER(*providers, provider);
588251881Speter
589251881Speter          SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
590251881Speter                                                          "gnome_keyring",
591251881Speter                                                          "ssl_client_cert_pw",
592251881Speter                                                          pool));
593251881Speter          SVN__MAYBE_ADD_PROVIDER(*providers, provider);
594251881Speter        }
595251881Speter      /* GPG-AGENT */
596251881Speter      else if (apr_strnatcmp(password_store, "gpg-agent") == 0)
597251881Speter        {
598251881Speter          SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
599251881Speter                                                          "gpg_agent",
600251881Speter                                                          "simple",
601251881Speter                                                          pool));
602251881Speter          SVN__MAYBE_ADD_PROVIDER(*providers, provider);
603251881Speter        }
604251881Speter      /* KWallet */
605251881Speter      else if (apr_strnatcmp(password_store, "kwallet") == 0)
606251881Speter        {
607251881Speter          SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
608251881Speter                                                          "kwallet",
609251881Speter                                                          "simple",
610251881Speter                                                          pool));
611251881Speter          SVN__MAYBE_ADD_PROVIDER(*providers, provider);
612251881Speter
613251881Speter          SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
614251881Speter                                                          "kwallet",
615251881Speter                                                          "ssl_client_cert_pw",
616251881Speter                                                          pool));
617251881Speter          SVN__MAYBE_ADD_PROVIDER(*providers, provider);
618251881Speter        }
619251881Speter      /* Keychain */
620251881Speter      else if (apr_strnatcmp(password_store, "keychain") == 0)
621251881Speter        {
622251881Speter          SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
623251881Speter                                                          "keychain",
624251881Speter                                                          "simple",
625251881Speter                                                          pool));
626251881Speter          SVN__MAYBE_ADD_PROVIDER(*providers, provider);
627251881Speter
628251881Speter          SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
629251881Speter                                                          "keychain",
630251881Speter                                                          "ssl_client_cert_pw",
631251881Speter                                                          pool));
632251881Speter          SVN__MAYBE_ADD_PROVIDER(*providers, provider);
633251881Speter        }
634251881Speter      /* Windows */
635251881Speter      else if (apr_strnatcmp(password_store, "windows-cryptoapi") == 0)
636251881Speter        {
637251881Speter          SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
638251881Speter                                                          "windows",
639251881Speter                                                          "simple",
640251881Speter                                                          pool));
641251881Speter          SVN__MAYBE_ADD_PROVIDER(*providers, provider);
642251881Speter
643251881Speter          SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
644251881Speter                                                          "windows",
645251881Speter                                                          "ssl_client_cert_pw",
646251881Speter                                                          pool));
647251881Speter          SVN__MAYBE_ADD_PROVIDER(*providers, provider);
648251881Speter        }
649251881Speter    }
650251881Speter
651251881Speter  return SVN_NO_ERROR;
652251881Speter}
653