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_auth_private.h"
39 #include "private/svn_dep_compat.h"
40 #include "private/svn_subr_private.h"
47 A good way to think of this machinery is as a set of tables.
49 - Each type of credentials selects a single table.
51 - In a given table, each row is a 'provider' capable of returning
52 the same type of credentials. Each column represents a
53 provider's repeated attempts to provide credentials.
56 Fetching Credentials from Providers
57 -----------------------------------
59 When the caller asks for a particular type of credentials, the
60 machinery in this file walks over the appropriate table. It starts
61 with the first provider (first row), and calls first_credentials()
62 to get the first set of credentials (first column). If the caller
63 is unhappy with the credentials, then each subsequent call to
64 next_credentials() traverses the row from left to right. If the
65 provider returns error at any point, then we go to the next provider
66 (row). We continue this way until every provider fails, or
67 until the client is happy with the returned credentials.
69 Note that the caller cannot see the table traversal, and thus has
70 no idea when we switch providers.
73 Storing Credentials with Providers
74 ----------------------------------
76 When the server has validated a set of credentials, and when
77 credential caching is enabled, we have the chance to store those
78 credentials for later use. The provider which provided the working
79 credentials is the first one given the opportunity to (re)cache
80 those credentials. Its save_credentials() function is invoked with
81 the working credentials. If that provider reports that it
82 successfully stored the credentials, we're done. Otherwise, we
83 walk the providers (rows) for that type of credentials in order
84 from the top of the table, allowing each in turn the opportunity to
85 store the credentials. When one reports that it has done so
86 successfully -- or when we run out of providers (rows) to try --
92 /* This effectively defines a single table. Every provider in this
93 array returns the same kind of credentials. */
94 typedef struct provider_set_t
96 /* ordered array of svn_auth_provider_object_t */
97 apr_array_header_t *providers;
102 /* The main auth baton. */
103 struct svn_auth_baton_t
105 /* a collection of tables. maps cred_kind -> provider_set */
108 /* the pool I'm allocated in. */
111 /* run-time parameters needed by providers. */
112 apr_hash_t *parameters;
114 /* run-time credentials cache. */
115 apr_hash_t *creds_cache;
119 /* Abstracted iteration baton */
120 struct svn_auth_iterstate_t
122 provider_set_t *table; /* the table being searched */
123 int provider_idx; /* the current provider (row) */
124 svn_boolean_t got_first; /* did we get the provider's first creds? */
125 void *provider_iter_baton; /* the provider's own iteration context */
126 const char *realmstring; /* The original realmstring passed in */
127 const char *cache_key; /* key to use in auth_baton's creds_cache */
128 svn_auth_baton_t *auth_baton; /* the original auth_baton. */
134 svn_auth_open(svn_auth_baton_t **auth_baton,
135 const apr_array_header_t *providers,
138 svn_auth_baton_t *ab;
139 svn_auth_provider_object_t *provider;
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->creds_cache = apr_hash_make(pool);
149 /* Register each provider in order. Providers of different
150 credentials will be automatically sorted into different tables by
151 register_provider(). */
152 for (i = 0; i < providers->nelts; i++)
154 provider_set_t *table;
155 provider = APR_ARRAY_IDX(providers, i, svn_auth_provider_object_t *);
157 /* Add it to the appropriate table in the auth_baton */
158 table = svn_hash_gets(ab->tables, provider->vtable->cred_kind);
161 table = apr_pcalloc(pool, sizeof(*table));
163 = apr_array_make(pool, 1, sizeof(svn_auth_provider_object_t *));
165 svn_hash_sets(ab->tables, provider->vtable->cred_kind, table);
167 APR_ARRAY_PUSH(table->providers, svn_auth_provider_object_t *)
177 svn_auth_set_parameter(svn_auth_baton_t *auth_baton,
181 svn_hash_sets(auth_baton->parameters, name, value);
185 svn_auth_get_parameter(svn_auth_baton_t *auth_baton,
188 return svn_hash_gets(auth_baton->parameters, name);
192 /* Return the key used to address the in-memory cache of auth
193 credentials of type CRED_KIND and associated with REALMSTRING. */
195 make_cache_key(const char *cred_kind,
196 const char *realmstring,
199 return apr_pstrcat(pool, cred_kind, ":", realmstring, (char *)NULL);
203 svn_auth_first_credentials(void **credentials,
204 svn_auth_iterstate_t **state,
205 const char *cred_kind,
206 const char *realmstring,
207 svn_auth_baton_t *auth_baton,
211 provider_set_t *table;
212 svn_auth_provider_object_t *provider = NULL;
214 void *iter_baton = NULL;
215 svn_boolean_t got_first = FALSE;
216 svn_auth_iterstate_t *iterstate;
217 const char *cache_key;
219 /* Get the appropriate table of providers for CRED_KIND. */
220 table = svn_hash_gets(auth_baton->tables, cred_kind);
222 return svn_error_createf(SVN_ERR_AUTHN_NO_PROVIDER, NULL,
223 _("No provider registered for '%s' credentials"),
226 /* First, see if we have cached creds in the auth_baton. */
227 cache_key = make_cache_key(cred_kind, realmstring, pool);
228 creds = svn_hash_gets(auth_baton->creds_cache, cache_key);
234 /* If not, find a provider that can give "first" credentials. */
236 /* Find a provider that can give "first" credentials. */
237 for (i = 0; i < table->providers->nelts; i++)
239 provider = APR_ARRAY_IDX(table->providers, i,
240 svn_auth_provider_object_t *);
241 SVN_ERR(provider->vtable->first_credentials(&creds, &iter_baton,
242 provider->provider_baton,
243 auth_baton->parameters,
259 /* Build an abstract iteration state. */
260 iterstate = apr_pcalloc(pool, sizeof(*iterstate));
261 iterstate->table = table;
262 iterstate->provider_idx = i;
263 iterstate->got_first = got_first;
264 iterstate->provider_iter_baton = iter_baton;
265 iterstate->realmstring = apr_pstrdup(pool, realmstring);
266 iterstate->cache_key = cache_key;
267 iterstate->auth_baton = auth_baton;
270 /* Put the creds in the cache */
271 svn_hash_sets(auth_baton->creds_cache,
272 apr_pstrdup(auth_baton->pool, cache_key),
276 *credentials = creds;
283 svn_auth_next_credentials(void **credentials,
284 svn_auth_iterstate_t *state,
287 svn_auth_baton_t *auth_baton = state->auth_baton;
288 svn_auth_provider_object_t *provider;
289 provider_set_t *table = state->table;
292 /* Continue traversing the table from where we left off. */
294 state->provider_idx < table->providers->nelts;
295 state->provider_idx++)
297 provider = APR_ARRAY_IDX(table->providers,
299 svn_auth_provider_object_t *);
300 if (! state->got_first)
302 SVN_ERR(provider->vtable->first_credentials(
303 &creds, &(state->provider_iter_baton),
304 provider->provider_baton, auth_baton->parameters,
305 state->realmstring, auth_baton->pool));
306 state->got_first = TRUE;
308 else if (provider->vtable->next_credentials)
310 SVN_ERR(provider->vtable->next_credentials(
311 &creds, state->provider_iter_baton,
312 provider->provider_baton, auth_baton->parameters,
313 state->realmstring, auth_baton->pool));
318 /* Put the creds in the cache */
319 svn_hash_sets(auth_baton->creds_cache, state->cache_key, creds);
323 state->got_first = FALSE;
326 *credentials = creds;
333 svn_auth_save_credentials(svn_auth_iterstate_t *state,
337 svn_auth_provider_object_t *provider;
338 svn_boolean_t save_succeeded = FALSE;
339 const char *no_auth_cache;
340 svn_auth_baton_t *auth_baton;
343 if (! state || state->table->providers->nelts <= state->provider_idx)
346 auth_baton = state->auth_baton;
347 creds = svn_hash_gets(state->auth_baton->creds_cache, state->cache_key);
351 /* Do not save the creds if SVN_AUTH_PARAM_NO_AUTH_CACHE is set */
352 no_auth_cache = svn_hash_gets(auth_baton->parameters,
353 SVN_AUTH_PARAM_NO_AUTH_CACHE);
357 /* First, try to save the creds using the provider that produced them. */
358 provider = APR_ARRAY_IDX(state->table->providers,
360 svn_auth_provider_object_t *);
361 if (provider->vtable->save_credentials)
362 SVN_ERR(provider->vtable->save_credentials(&save_succeeded,
364 provider->provider_baton,
365 auth_baton->parameters,
371 /* Otherwise, loop from the top of the list, asking every provider
372 to attempt a save. ### todo: someday optimize so we don't
373 necessarily start from the top of the list. */
374 for (i = 0; i < state->table->providers->nelts; i++)
376 provider = APR_ARRAY_IDX(state->table->providers, i,
377 svn_auth_provider_object_t *);
378 if (provider->vtable->save_credentials)
379 SVN_ERR(provider->vtable->save_credentials
380 (&save_succeeded, creds,
381 provider->provider_baton,
382 auth_baton->parameters,
390 /* ### notice that at the moment, if no provider can save, there's
391 no way the caller will know. */
398 svn_auth_forget_credentials(svn_auth_baton_t *auth_baton,
399 const char *cred_kind,
400 const char *realmstring,
401 apr_pool_t *scratch_pool)
403 SVN_ERR_ASSERT((cred_kind && realmstring) || (!cred_kind && !realmstring));
405 /* If we have a CRED_KIND and REALMSTRING, we clear out just the
406 cached item (if any). Otherwise, empty the whole hash. */
409 svn_hash_sets(auth_baton->creds_cache,
410 make_cache_key(cred_kind, realmstring, scratch_pool),
415 apr_hash_clear(auth_baton->creds_cache);
422 svn_auth_ssl_server_cert_info_t *
423 svn_auth_ssl_server_cert_info_dup
424 (const svn_auth_ssl_server_cert_info_t *info, apr_pool_t *pool)
426 svn_auth_ssl_server_cert_info_t *new_info
427 = apr_palloc(pool, sizeof(*new_info));
431 new_info->hostname = apr_pstrdup(pool, new_info->hostname);
432 new_info->fingerprint = apr_pstrdup(pool, new_info->fingerprint);
433 new_info->valid_from = apr_pstrdup(pool, new_info->valid_from);
434 new_info->valid_until = apr_pstrdup(pool, new_info->valid_until);
435 new_info->issuer_dname = apr_pstrdup(pool, new_info->issuer_dname);
436 new_info->ascii_cert = apr_pstrdup(pool, new_info->ascii_cert);
442 svn_auth_get_platform_specific_provider(svn_auth_provider_object_t **provider,
443 const char *provider_name,
444 const char *provider_type,
449 if (apr_strnatcmp(provider_name, "gnome_keyring") == 0 ||
450 apr_strnatcmp(provider_name, "kwallet") == 0)
452 #if defined(SVN_HAVE_GNOME_KEYRING) || defined(SVN_HAVE_KWALLET)
453 apr_dso_handle_t *dso;
454 apr_dso_handle_sym_t provider_function_symbol, version_function_symbol;
455 const char *library_label, *library_name;
456 const char *provider_function_name, *version_function_name;
457 library_name = apr_psprintf(pool,
458 "libsvn_auth_%s-%d.so.%d",
460 SVN_VER_MAJOR, SVN_SOVERSION);
461 library_label = apr_psprintf(pool, "svn_%s", provider_name);
462 provider_function_name = apr_psprintf(pool,
463 "svn_auth_get_%s_%s_provider",
464 provider_name, provider_type);
465 version_function_name = apr_psprintf(pool,
466 "svn_auth_%s_version",
468 SVN_ERR(svn_dso_load(&dso, library_name));
471 if (apr_dso_sym(&version_function_symbol,
473 version_function_name) == 0)
475 svn_version_func_t version_function
476 = version_function_symbol;
477 svn_version_checklist_t check_list[2];
479 check_list[0].label = library_label;
480 check_list[0].version_query = version_function;
481 check_list[1].label = NULL;
482 check_list[1].version_query = NULL;
483 SVN_ERR(svn_ver_check_list2(svn_subr_version(), check_list,
486 if (apr_dso_sym(&provider_function_symbol,
488 provider_function_name) == 0)
490 if (strcmp(provider_type, "simple") == 0)
492 svn_auth_simple_provider_func_t provider_function
493 = provider_function_symbol;
494 provider_function(provider, pool);
496 else if (strcmp(provider_type, "ssl_client_cert_pw") == 0)
498 svn_auth_ssl_client_cert_pw_provider_func_t provider_function
499 = provider_function_symbol;
500 provider_function(provider, pool);
508 #if defined(SVN_HAVE_GPG_AGENT)
509 if (strcmp(provider_name, "gpg_agent") == 0 &&
510 strcmp(provider_type, "simple") == 0)
512 svn_auth_get_gpg_agent_simple_provider(provider, pool);
515 #ifdef SVN_HAVE_KEYCHAIN_SERVICES
516 if (strcmp(provider_name, "keychain") == 0 &&
517 strcmp(provider_type, "simple") == 0)
519 svn_auth_get_keychain_simple_provider(provider, pool);
521 else if (strcmp(provider_name, "keychain") == 0 &&
522 strcmp(provider_type, "ssl_client_cert_pw") == 0)
524 svn_auth_get_keychain_ssl_client_cert_pw_provider(provider, pool);
528 #if defined(WIN32) && !defined(__MINGW32__)
529 if (strcmp(provider_name, "windows") == 0 &&
530 strcmp(provider_type, "simple") == 0)
532 svn_auth_get_windows_simple_provider(provider, pool);
534 else if (strcmp(provider_name, "windows") == 0 &&
535 strcmp(provider_type, "ssl_client_cert_pw") == 0)
537 svn_auth_get_windows_ssl_client_cert_pw_provider(provider, pool);
539 else if (strcmp(provider_name, "windows") == 0 &&
540 strcmp(provider_type, "ssl_server_trust") == 0)
542 svn_auth_get_windows_ssl_server_trust_provider(provider, pool);
544 else if (strcmp(provider_name, "windows") == 0 &&
545 strcmp(provider_type, "ssl_server_authority") == 0)
547 svn_auth__get_windows_ssl_server_authority_provider(provider, pool);
556 svn_auth_get_platform_specific_client_providers(apr_array_header_t **providers,
557 svn_config_t *config,
560 svn_auth_provider_object_t *provider;
561 const char *password_stores_config_option;
562 apr_array_header_t *password_stores;
565 #define SVN__MAYBE_ADD_PROVIDER(list, p) \
566 { if (p) APR_ARRAY_PUSH(list, svn_auth_provider_object_t *) = p; }
568 #define SVN__DEFAULT_AUTH_PROVIDER_LIST \
569 "gnome-keyring,kwallet,keychain,gpg-agent,windows-cryptoapi"
571 *providers = apr_array_make(pool, 12, sizeof(svn_auth_provider_object_t *));
573 /* Fetch the configured list of password stores, and split them into
575 svn_config_get(config,
576 &password_stores_config_option,
577 SVN_CONFIG_SECTION_AUTH,
578 SVN_CONFIG_OPTION_PASSWORD_STORES,
579 SVN__DEFAULT_AUTH_PROVIDER_LIST);
580 password_stores = svn_cstring_split(password_stores_config_option,
583 for (i = 0; i < password_stores->nelts; i++)
585 const char *password_store = APR_ARRAY_IDX(password_stores, i,
589 if (apr_strnatcmp(password_store, "gnome-keyring") == 0)
591 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
595 SVN__MAYBE_ADD_PROVIDER(*providers, provider);
597 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
599 "ssl_client_cert_pw",
601 SVN__MAYBE_ADD_PROVIDER(*providers, provider);
604 else if (apr_strnatcmp(password_store, "gpg-agent") == 0)
606 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
610 SVN__MAYBE_ADD_PROVIDER(*providers, provider);
613 else if (apr_strnatcmp(password_store, "kwallet") == 0)
615 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
619 SVN__MAYBE_ADD_PROVIDER(*providers, provider);
621 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
623 "ssl_client_cert_pw",
625 SVN__MAYBE_ADD_PROVIDER(*providers, provider);
628 else if (apr_strnatcmp(password_store, "keychain") == 0)
630 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
634 SVN__MAYBE_ADD_PROVIDER(*providers, provider);
636 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
638 "ssl_client_cert_pw",
640 SVN__MAYBE_ADD_PROVIDER(*providers, provider);
643 else if (apr_strnatcmp(password_store, "windows-cryptoapi") == 0)
645 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
649 SVN__MAYBE_ADD_PROVIDER(*providers, provider);
651 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
653 "ssl_client_cert_pw",
655 SVN__MAYBE_ADD_PROVIDER(*providers, provider);