2 * auth.c: authentication support functions for Subversion
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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
21 * ====================================================================
25 #include <apr_pools.h>
26 #include <apr_tables.h>
27 #include <apr_strings.h>
30 #include "svn_types.h"
31 #include "svn_string.h"
32 #include "svn_error.h"
34 #include "svn_config.h"
35 #include "svn_private_config.h"
37 #include "svn_version.h"
38 #include "private/svn_dep_compat.h"
45 A good way to think of this machinery is as a set of tables.
47 - Each type of credentials selects a single table.
49 - In a given table, each row is a 'provider' capable of returning
50 the same type of credentials. Each column represents a
51 provider's repeated attempts to provide credentials.
54 Fetching Credentials from Providers
55 -----------------------------------
57 When the caller asks for a particular type of credentials, the
58 machinery in this file walks over the appropriate table. It starts
59 with the first provider (first row), and calls first_credentials()
60 to get the first set of credentials (first column). If the caller
61 is unhappy with the credentials, then each subsequent call to
62 next_credentials() traverses the row from left to right. If the
63 provider returns error at any point, then we go to the next provider
64 (row). We continue this way until every provider fails, or
65 until the client is happy with the returned credentials.
67 Note that the caller cannot see the table traversal, and thus has
68 no idea when we switch providers.
71 Storing Credentials with Providers
72 ----------------------------------
74 When the server has validated a set of credentials, and when
75 credential caching is enabled, we have the chance to store those
76 credentials for later use. The provider which provided the working
77 credentials is the first one given the opportunity to (re)cache
78 those credentials. Its save_credentials() function is invoked with
79 the working credentials. If that provider reports that it
80 successfully stored the credentials, we're done. Otherwise, we
81 walk the providers (rows) for that type of credentials in order
82 from the top of the table, allowing each in turn the opportunity to
83 store the credentials. When one reports that it has done so
84 successfully -- or when we run out of providers (rows) to try --
90 /* This effectively defines a single table. Every provider in this
91 array returns the same kind of credentials. */
92 typedef struct provider_set_t
94 /* ordered array of svn_auth_provider_object_t */
95 apr_array_header_t *providers;
100 /* The main auth baton. */
101 struct svn_auth_baton_t
103 /* a collection of tables. maps cred_kind -> provider_set */
106 /* the pool I'm allocated in. */
109 /* run-time parameters needed by providers. */
110 apr_hash_t *parameters;
112 /* run-time credentials cache. */
113 apr_hash_t *creds_cache;
117 /* Abstracted iteration baton */
118 struct svn_auth_iterstate_t
120 provider_set_t *table; /* the table being searched */
121 int provider_idx; /* the current provider (row) */
122 svn_boolean_t got_first; /* did we get the provider's first creds? */
123 void *provider_iter_baton; /* the provider's own iteration context */
124 const char *realmstring; /* The original realmstring passed in */
125 const char *cache_key; /* key to use in auth_baton's creds_cache */
126 svn_auth_baton_t *auth_baton; /* the original auth_baton. */
132 svn_auth_open(svn_auth_baton_t **auth_baton,
133 const apr_array_header_t *providers,
136 svn_auth_baton_t *ab;
137 svn_auth_provider_object_t *provider;
140 /* Build the auth_baton. */
141 ab = apr_pcalloc(pool, sizeof(*ab));
142 ab->tables = apr_hash_make(pool);
143 ab->parameters = apr_hash_make(pool);
144 ab->creds_cache = apr_hash_make(pool);
147 /* Register each provider in order. Providers of different
148 credentials will be automatically sorted into different tables by
149 register_provider(). */
150 for (i = 0; i < providers->nelts; i++)
152 provider_set_t *table;
153 provider = APR_ARRAY_IDX(providers, i, svn_auth_provider_object_t *);
155 /* Add it to the appropriate table in the auth_baton */
156 table = svn_hash_gets(ab->tables, provider->vtable->cred_kind);
159 table = apr_pcalloc(pool, sizeof(*table));
161 = apr_array_make(pool, 1, sizeof(svn_auth_provider_object_t *));
163 svn_hash_sets(ab->tables, provider->vtable->cred_kind, table);
165 APR_ARRAY_PUSH(table->providers, svn_auth_provider_object_t *)
175 svn_auth_set_parameter(svn_auth_baton_t *auth_baton,
179 svn_hash_sets(auth_baton->parameters, name, value);
183 svn_auth_get_parameter(svn_auth_baton_t *auth_baton,
186 return svn_hash_gets(auth_baton->parameters, name);
190 /* Return the key used to address the in-memory cache of auth
191 credentials of type CRED_KIND and associated with REALMSTRING. */
193 make_cache_key(const char *cred_kind,
194 const char *realmstring,
197 return apr_pstrcat(pool, cred_kind, ":", realmstring, (char *)NULL);
201 svn_auth_first_credentials(void **credentials,
202 svn_auth_iterstate_t **state,
203 const char *cred_kind,
204 const char *realmstring,
205 svn_auth_baton_t *auth_baton,
209 provider_set_t *table;
210 svn_auth_provider_object_t *provider = NULL;
212 void *iter_baton = NULL;
213 svn_boolean_t got_first = FALSE;
214 svn_auth_iterstate_t *iterstate;
215 const char *cache_key;
217 /* Get the appropriate table of providers for CRED_KIND. */
218 table = svn_hash_gets(auth_baton->tables, cred_kind);
220 return svn_error_createf(SVN_ERR_AUTHN_NO_PROVIDER, NULL,
221 _("No provider registered for '%s' credentials"),
224 /* First, see if we have cached creds in the auth_baton. */
225 cache_key = make_cache_key(cred_kind, realmstring, pool);
226 creds = svn_hash_gets(auth_baton->creds_cache, cache_key);
232 /* If not, find a provider that can give "first" credentials. */
234 /* Find a provider that can give "first" credentials. */
235 for (i = 0; i < table->providers->nelts; i++)
237 provider = APR_ARRAY_IDX(table->providers, i,
238 svn_auth_provider_object_t *);
239 SVN_ERR(provider->vtable->first_credentials(&creds, &iter_baton,
240 provider->provider_baton,
241 auth_baton->parameters,
257 /* Build an abstract iteration state. */
258 iterstate = apr_pcalloc(pool, sizeof(*iterstate));
259 iterstate->table = table;
260 iterstate->provider_idx = i;
261 iterstate->got_first = got_first;
262 iterstate->provider_iter_baton = iter_baton;
263 iterstate->realmstring = apr_pstrdup(pool, realmstring);
264 iterstate->cache_key = cache_key;
265 iterstate->auth_baton = auth_baton;
268 /* Put the creds in the cache */
269 svn_hash_sets(auth_baton->creds_cache,
270 apr_pstrdup(auth_baton->pool, cache_key),
274 *credentials = creds;
281 svn_auth_next_credentials(void **credentials,
282 svn_auth_iterstate_t *state,
285 svn_auth_baton_t *auth_baton = state->auth_baton;
286 svn_auth_provider_object_t *provider;
287 provider_set_t *table = state->table;
290 /* Continue traversing the table from where we left off. */
292 state->provider_idx < table->providers->nelts;
293 state->provider_idx++)
295 provider = APR_ARRAY_IDX(table->providers,
297 svn_auth_provider_object_t *);
298 if (! state->got_first)
300 SVN_ERR(provider->vtable->first_credentials(
301 &creds, &(state->provider_iter_baton),
302 provider->provider_baton, auth_baton->parameters,
303 state->realmstring, auth_baton->pool));
304 state->got_first = TRUE;
306 else if (provider->vtable->next_credentials)
308 SVN_ERR(provider->vtable->next_credentials(
309 &creds, state->provider_iter_baton,
310 provider->provider_baton, auth_baton->parameters,
311 state->realmstring, auth_baton->pool));
316 /* Put the creds in the cache */
317 svn_hash_sets(auth_baton->creds_cache, state->cache_key, creds);
321 state->got_first = FALSE;
324 *credentials = creds;
331 svn_auth_save_credentials(svn_auth_iterstate_t *state,
335 svn_auth_provider_object_t *provider;
336 svn_boolean_t save_succeeded = FALSE;
337 const char *no_auth_cache;
338 svn_auth_baton_t *auth_baton;
341 if (! state || state->table->providers->nelts <= state->provider_idx)
344 auth_baton = state->auth_baton;
345 creds = svn_hash_gets(state->auth_baton->creds_cache, state->cache_key);
349 /* Do not save the creds if SVN_AUTH_PARAM_NO_AUTH_CACHE is set */
350 no_auth_cache = svn_hash_gets(auth_baton->parameters,
351 SVN_AUTH_PARAM_NO_AUTH_CACHE);
355 /* First, try to save the creds using the provider that produced them. */
356 provider = APR_ARRAY_IDX(state->table->providers,
358 svn_auth_provider_object_t *);
359 if (provider->vtable->save_credentials)
360 SVN_ERR(provider->vtable->save_credentials(&save_succeeded,
362 provider->provider_baton,
363 auth_baton->parameters,
369 /* Otherwise, loop from the top of the list, asking every provider
370 to attempt a save. ### todo: someday optimize so we don't
371 necessarily start from the top of the list. */
372 for (i = 0; i < state->table->providers->nelts; i++)
374 provider = APR_ARRAY_IDX(state->table->providers, i,
375 svn_auth_provider_object_t *);
376 if (provider->vtable->save_credentials)
377 SVN_ERR(provider->vtable->save_credentials
378 (&save_succeeded, creds,
379 provider->provider_baton,
380 auth_baton->parameters,
388 /* ### notice that at the moment, if no provider can save, there's
389 no way the caller will know. */
396 svn_auth_forget_credentials(svn_auth_baton_t *auth_baton,
397 const char *cred_kind,
398 const char *realmstring,
399 apr_pool_t *scratch_pool)
401 SVN_ERR_ASSERT((cred_kind && realmstring) || (!cred_kind && !realmstring));
403 /* If we have a CRED_KIND and REALMSTRING, we clear out just the
404 cached item (if any). Otherwise, empty the whole hash. */
407 svn_hash_sets(auth_baton->creds_cache,
408 make_cache_key(cred_kind, realmstring, scratch_pool),
413 apr_hash_clear(auth_baton->creds_cache);
420 svn_auth_ssl_server_cert_info_t *
421 svn_auth_ssl_server_cert_info_dup
422 (const svn_auth_ssl_server_cert_info_t *info, apr_pool_t *pool)
424 svn_auth_ssl_server_cert_info_t *new_info
425 = apr_palloc(pool, sizeof(*new_info));
429 new_info->hostname = apr_pstrdup(pool, new_info->hostname);
430 new_info->fingerprint = apr_pstrdup(pool, new_info->fingerprint);
431 new_info->valid_from = apr_pstrdup(pool, new_info->valid_from);
432 new_info->valid_until = apr_pstrdup(pool, new_info->valid_until);
433 new_info->issuer_dname = apr_pstrdup(pool, new_info->issuer_dname);
434 new_info->ascii_cert = apr_pstrdup(pool, new_info->ascii_cert);
440 svn_auth_get_platform_specific_provider(svn_auth_provider_object_t **provider,
441 const char *provider_name,
442 const char *provider_type,
447 if (apr_strnatcmp(provider_name, "gnome_keyring") == 0 ||
448 apr_strnatcmp(provider_name, "kwallet") == 0)
450 #if defined(SVN_HAVE_GNOME_KEYRING) || defined(SVN_HAVE_KWALLET)
451 apr_dso_handle_t *dso;
452 apr_dso_handle_sym_t provider_function_symbol, version_function_symbol;
453 const char *library_label, *library_name;
454 const char *provider_function_name, *version_function_name;
455 library_name = apr_psprintf(pool,
456 "libsvn_auth_%s-%d.so.%d",
458 SVN_VER_MAJOR, SVN_SOVERSION);
459 library_label = apr_psprintf(pool, "svn_%s", provider_name);
460 provider_function_name = apr_psprintf(pool,
461 "svn_auth_get_%s_%s_provider",
462 provider_name, provider_type);
463 version_function_name = apr_psprintf(pool,
464 "svn_auth_%s_version",
466 SVN_ERR(svn_dso_load(&dso, library_name));
469 if (apr_dso_sym(&version_function_symbol,
471 version_function_name) == 0)
473 svn_version_func_t version_function
474 = version_function_symbol;
475 svn_version_checklist_t check_list[2];
477 check_list[0].label = library_label;
478 check_list[0].version_query = version_function;
479 check_list[1].label = NULL;
480 check_list[1].version_query = NULL;
481 SVN_ERR(svn_ver_check_list(svn_subr_version(), check_list));
483 if (apr_dso_sym(&provider_function_symbol,
485 provider_function_name) == 0)
487 if (strcmp(provider_type, "simple") == 0)
489 svn_auth_simple_provider_func_t provider_function
490 = provider_function_symbol;
491 provider_function(provider, pool);
493 else if (strcmp(provider_type, "ssl_client_cert_pw") == 0)
495 svn_auth_ssl_client_cert_pw_provider_func_t provider_function
496 = provider_function_symbol;
497 provider_function(provider, pool);
505 #if defined(SVN_HAVE_GPG_AGENT)
506 if (strcmp(provider_name, "gpg_agent") == 0 &&
507 strcmp(provider_type, "simple") == 0)
509 svn_auth_get_gpg_agent_simple_provider(provider, pool);
512 #ifdef SVN_HAVE_KEYCHAIN_SERVICES
513 if (strcmp(provider_name, "keychain") == 0 &&
514 strcmp(provider_type, "simple") == 0)
516 svn_auth_get_keychain_simple_provider(provider, pool);
518 else if (strcmp(provider_name, "keychain") == 0 &&
519 strcmp(provider_type, "ssl_client_cert_pw") == 0)
521 svn_auth_get_keychain_ssl_client_cert_pw_provider(provider, pool);
525 #if defined(WIN32) && !defined(__MINGW32__)
526 if (strcmp(provider_name, "windows") == 0 &&
527 strcmp(provider_type, "simple") == 0)
529 svn_auth_get_windows_simple_provider(provider, pool);
531 else if (strcmp(provider_name, "windows") == 0 &&
532 strcmp(provider_type, "ssl_client_cert_pw") == 0)
534 svn_auth_get_windows_ssl_client_cert_pw_provider(provider, pool);
536 else if (strcmp(provider_name, "windows") == 0 &&
537 strcmp(provider_type, "ssl_server_trust") == 0)
539 svn_auth_get_windows_ssl_server_trust_provider(provider, pool);
548 svn_auth_get_platform_specific_client_providers(apr_array_header_t **providers,
549 svn_config_t *config,
552 svn_auth_provider_object_t *provider;
553 const char *password_stores_config_option;
554 apr_array_header_t *password_stores;
557 #define SVN__MAYBE_ADD_PROVIDER(list, p) \
558 { if (p) APR_ARRAY_PUSH(list, svn_auth_provider_object_t *) = p; }
560 #define SVN__DEFAULT_AUTH_PROVIDER_LIST \
561 "gnome-keyring,kwallet,keychain,gpg-agent,windows-cryptoapi"
563 *providers = apr_array_make(pool, 12, sizeof(svn_auth_provider_object_t *));
565 /* Fetch the configured list of password stores, and split them into
567 svn_config_get(config,
568 &password_stores_config_option,
569 SVN_CONFIG_SECTION_AUTH,
570 SVN_CONFIG_OPTION_PASSWORD_STORES,
571 SVN__DEFAULT_AUTH_PROVIDER_LIST);
572 password_stores = svn_cstring_split(password_stores_config_option,
575 for (i = 0; i < password_stores->nelts; i++)
577 const char *password_store = APR_ARRAY_IDX(password_stores, i,
581 if (apr_strnatcmp(password_store, "gnome-keyring") == 0)
583 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
587 SVN__MAYBE_ADD_PROVIDER(*providers, provider);
589 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
591 "ssl_client_cert_pw",
593 SVN__MAYBE_ADD_PROVIDER(*providers, provider);
596 else if (apr_strnatcmp(password_store, "gpg-agent") == 0)
598 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
602 SVN__MAYBE_ADD_PROVIDER(*providers, provider);
605 else if (apr_strnatcmp(password_store, "kwallet") == 0)
607 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
611 SVN__MAYBE_ADD_PROVIDER(*providers, provider);
613 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
615 "ssl_client_cert_pw",
617 SVN__MAYBE_ADD_PROVIDER(*providers, provider);
620 else if (apr_strnatcmp(password_store, "keychain") == 0)
622 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
626 SVN__MAYBE_ADD_PROVIDER(*providers, provider);
628 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
630 "ssl_client_cert_pw",
632 SVN__MAYBE_ADD_PROVIDER(*providers, provider);
635 else if (apr_strnatcmp(password_store, "windows-cryptoapi") == 0)
637 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
641 SVN__MAYBE_ADD_PROVIDER(*providers, provider);
643 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
645 "ssl_client_cert_pw",
647 SVN__MAYBE_ADD_PROVIDER(*providers, provider);