#include <apr_pools.h>
#include <apr_tables.h>
#include <apr_strings.h>
#include "svn_hash.h"
#include "svn_types.h"
#include "svn_string.h"
#include "svn_error.h"
#include "svn_auth.h"
#include "svn_config.h"
#include "svn_private_config.h"
#include "svn_dso.h"
#include "svn_version.h"
#include "private/svn_auth_private.h"
#include "private/svn_dep_compat.h"
#include "auth.h"
typedef struct provider_set_t
{
apr_array_header_t *providers;
} provider_set_t;
struct svn_auth_baton_t
{
apr_hash_t *tables;
apr_pool_t *pool;
apr_hash_t *parameters;
apr_hash_t *slave_parameters;
apr_hash_t *creds_cache;
};
struct svn_auth_iterstate_t
{
provider_set_t *table;
int provider_idx;
svn_boolean_t got_first;
void *provider_iter_baton;
const char *realmstring;
const char *cache_key;
svn_auth_baton_t *auth_baton;
apr_hash_t *parameters;
};
void
svn_auth_open(svn_auth_baton_t **auth_baton,
const apr_array_header_t *providers,
apr_pool_t *pool)
{
svn_auth_baton_t *ab;
svn_auth_provider_object_t *provider;
int i;
ab = apr_pcalloc(pool, sizeof(*ab));
ab->tables = apr_hash_make(pool);
ab->parameters = apr_hash_make(pool);
ab->creds_cache = apr_hash_make(pool);
ab->pool = pool;
for (i = 0; i < providers->nelts; i++)
{
provider_set_t *table;
provider = APR_ARRAY_IDX(providers, i, svn_auth_provider_object_t *);
table = svn_hash_gets(ab->tables, provider->vtable->cred_kind);
if (! table)
{
table = apr_pcalloc(pool, sizeof(*table));
table->providers
= apr_array_make(pool, 1, sizeof(svn_auth_provider_object_t *));
svn_hash_sets(ab->tables, provider->vtable->cred_kind, table);
}
APR_ARRAY_PUSH(table->providers, svn_auth_provider_object_t *)
= provider;
}
*auth_baton = ab;
}
static const void *auth_NULL = NULL;
void
svn_auth_set_parameter(svn_auth_baton_t *auth_baton,
const char *name,
const void *value)
{
if (auth_baton)
{
if (auth_baton->slave_parameters)
{
if (!value)
value = &auth_NULL;
svn_hash_sets(auth_baton->slave_parameters, name, value);
}
else
svn_hash_sets(auth_baton->parameters, name, value);
}
}
const void *
svn_auth_get_parameter(svn_auth_baton_t *auth_baton,
const char *name)
{
const void *value;
if (!auth_baton)
return NULL;
else if (!auth_baton->slave_parameters)
return svn_hash_gets(auth_baton->parameters, name);
value = svn_hash_gets(auth_baton->slave_parameters, name);
if (value)
return (value == &auth_NULL ? NULL : value);
return svn_hash_gets(auth_baton->parameters, name);
}
static const char *
make_cache_key(const char *cred_kind,
const char *realmstring,
apr_pool_t *pool)
{
return apr_pstrcat(pool, cred_kind, ":", realmstring, SVN_VA_NULL);
}
svn_error_t *
svn_auth_first_credentials(void **credentials,
svn_auth_iterstate_t **state,
const char *cred_kind,
const char *realmstring,
svn_auth_baton_t *auth_baton,
apr_pool_t *pool)
{
int i = 0;
provider_set_t *table;
svn_auth_provider_object_t *provider = NULL;
void *creds = NULL;
void *iter_baton = NULL;
svn_boolean_t got_first = FALSE;
svn_auth_iterstate_t *iterstate;
const char *cache_key;
apr_hash_t *parameters;
if (! auth_baton)
return svn_error_create(SVN_ERR_AUTHN_NO_PROVIDER, NULL,
_("No authentication providers registered"));
table = svn_hash_gets(auth_baton->tables, cred_kind);
if (! table)
return svn_error_createf(SVN_ERR_AUTHN_NO_PROVIDER, NULL,
_("No provider registered for '%s' credentials"),
cred_kind);
if (auth_baton->slave_parameters)
{
apr_hash_index_t *hi;
parameters = apr_hash_copy(pool, auth_baton->parameters);
for (hi = apr_hash_first(pool, auth_baton->slave_parameters);
hi;
hi = apr_hash_next(hi))
{
const void *value = apr_hash_this_val(hi);
if (value == &auth_NULL)
value = NULL;
svn_hash_sets(parameters, apr_hash_this_key(hi), value);
}
}
else
parameters = auth_baton->parameters;
cache_key = make_cache_key(cred_kind, realmstring, pool);
creds = svn_hash_gets(auth_baton->creds_cache, cache_key);
if (creds)
{
got_first = FALSE;
}
else
{
for (i = 0; i < table->providers->nelts; i++)
{
provider = APR_ARRAY_IDX(table->providers, i,
svn_auth_provider_object_t *);
SVN_ERR(provider->vtable->first_credentials(&creds, &iter_baton,
provider->provider_baton,
parameters,
realmstring,
auth_baton->pool));
if (creds != NULL)
{
got_first = TRUE;
break;
}
}
}
if (! creds)
{
*state = NULL;
}
else
{
iterstate = apr_pcalloc(pool, sizeof(*iterstate));
iterstate->table = table;
iterstate->provider_idx = i;
iterstate->got_first = got_first;
iterstate->provider_iter_baton = iter_baton;
iterstate->realmstring = apr_pstrdup(pool, realmstring);
iterstate->cache_key = cache_key;
iterstate->auth_baton = auth_baton;
iterstate->parameters = parameters;
*state = iterstate;
svn_hash_sets(auth_baton->creds_cache,
apr_pstrdup(auth_baton->pool, cache_key),
creds);
}
*credentials = creds;
return SVN_NO_ERROR;
}
svn_error_t *
svn_auth_next_credentials(void **credentials,
svn_auth_iterstate_t *state,
apr_pool_t *pool)
{
svn_auth_baton_t *auth_baton = state->auth_baton;
svn_auth_provider_object_t *provider;
provider_set_t *table = state->table;
void *creds = NULL;
for (;
state->provider_idx < table->providers->nelts;
state->provider_idx++)
{
provider = APR_ARRAY_IDX(table->providers,
state->provider_idx,
svn_auth_provider_object_t *);
if (! state->got_first)
{
SVN_ERR(provider->vtable->first_credentials(
&creds, &(state->provider_iter_baton),
provider->provider_baton, state->parameters,
state->realmstring, auth_baton->pool));
state->got_first = TRUE;
}
else if (provider->vtable->next_credentials)
{
SVN_ERR(provider->vtable->next_credentials(&creds,
state->provider_iter_baton,
provider->provider_baton,
state->parameters,
state->realmstring,
auth_baton->pool));
}
if (creds != NULL)
{
svn_hash_sets(auth_baton->creds_cache,
apr_pstrdup(auth_baton->pool, state->cache_key),
creds);
break;
}
state->got_first = FALSE;
}
*credentials = creds;
return SVN_NO_ERROR;
}
svn_error_t *
svn_auth_save_credentials(svn_auth_iterstate_t *state,
apr_pool_t *pool)
{
int i;
svn_auth_provider_object_t *provider;
svn_boolean_t save_succeeded = FALSE;
const char *no_auth_cache;
void *creds;
if (! state || state->table->providers->nelts <= state->provider_idx)
return SVN_NO_ERROR;
creds = svn_hash_gets(state->auth_baton->creds_cache, state->cache_key);
if (! creds)
return SVN_NO_ERROR;
no_auth_cache = svn_hash_gets(state->parameters,
SVN_AUTH_PARAM_NO_AUTH_CACHE);
if (no_auth_cache)
return SVN_NO_ERROR;
provider = APR_ARRAY_IDX(state->table->providers,
state->provider_idx,
svn_auth_provider_object_t *);
if (provider->vtable->save_credentials)
SVN_ERR(provider->vtable->save_credentials(&save_succeeded,
creds,
provider->provider_baton,
state->parameters,
state->realmstring,
pool));
if (save_succeeded)
return SVN_NO_ERROR;
for (i = 0; i < state->table->providers->nelts; i++)
{
provider = APR_ARRAY_IDX(state->table->providers, i,
svn_auth_provider_object_t *);
if (provider->vtable->save_credentials)
SVN_ERR(provider->vtable->save_credentials(&save_succeeded, creds,
provider->provider_baton,
state->parameters,
state->realmstring,
pool));
if (save_succeeded)
break;
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_auth_forget_credentials(svn_auth_baton_t *auth_baton,
const char *cred_kind,
const char *realmstring,
apr_pool_t *scratch_pool)
{
SVN_ERR_ASSERT((cred_kind && realmstring) || (!cred_kind && !realmstring));
if (cred_kind)
{
svn_hash_sets(auth_baton->creds_cache,
make_cache_key(cred_kind, realmstring, scratch_pool),
NULL);
}
else
{
apr_hash_clear(auth_baton->creds_cache);
}
return SVN_NO_ERROR;
}
svn_auth_ssl_server_cert_info_t *
svn_auth_ssl_server_cert_info_dup
(const svn_auth_ssl_server_cert_info_t *info, apr_pool_t *pool)
{
svn_auth_ssl_server_cert_info_t *new_info
= apr_palloc(pool, sizeof(*new_info));
*new_info = *info;
new_info->hostname = apr_pstrdup(pool, new_info->hostname);
new_info->fingerprint = apr_pstrdup(pool, new_info->fingerprint);
new_info->valid_from = apr_pstrdup(pool, new_info->valid_from);
new_info->valid_until = apr_pstrdup(pool, new_info->valid_until);
new_info->issuer_dname = apr_pstrdup(pool, new_info->issuer_dname);
new_info->ascii_cert = apr_pstrdup(pool, new_info->ascii_cert);
return new_info;
}
svn_error_t *
svn_auth_get_platform_specific_provider(svn_auth_provider_object_t **provider,
const char *provider_name,
const char *provider_type,
apr_pool_t *pool)
{
*provider = NULL;
if (apr_strnatcmp(provider_name, "gnome_keyring") == 0 ||
apr_strnatcmp(provider_name, "kwallet") == 0)
{
#if defined(SVN_HAVE_GNOME_KEYRING) || defined(SVN_HAVE_KWALLET) || defined (SVN_HAVE_LIBSECRET)
apr_dso_handle_t *dso;
apr_dso_handle_sym_t provider_function_symbol, version_function_symbol;
const char *library_label, *library_name;
const char *provider_function_name, *version_function_name;
library_name = apr_psprintf(pool,
"libsvn_auth_%s-%d.so.%d",
provider_name,
SVN_VER_MAJOR, SVN_SOVERSION);
library_label = apr_psprintf(pool, "svn_%s", provider_name);
provider_function_name = apr_psprintf(pool,
"svn_auth_get_%s_%s_provider",
provider_name, provider_type);
version_function_name = apr_psprintf(pool,
"svn_auth_%s_version",
provider_name);
SVN_ERR(svn_dso_load(&dso, library_name));
if (dso)
{
if (apr_dso_sym(&version_function_symbol,
dso,
version_function_name) == 0)
{
svn_version_func_t version_function
= version_function_symbol;
svn_version_checklist_t check_list[2];
check_list[0].label = library_label;
check_list[0].version_query = version_function;
check_list[1].label = NULL;
check_list[1].version_query = NULL;
SVN_ERR(svn_ver_check_list2(svn_subr_version(), check_list,
svn_ver_equal));
}
if (apr_dso_sym(&provider_function_symbol,
dso,
provider_function_name) == 0)
{
if (strcmp(provider_type, "simple") == 0)
{
svn_auth_simple_provider_func_t provider_function
= provider_function_symbol;
provider_function(provider, pool);
}
else if (strcmp(provider_type, "ssl_client_cert_pw") == 0)
{
svn_auth_ssl_client_cert_pw_provider_func_t provider_function
= provider_function_symbol;
provider_function(provider, pool);
}
}
}
#endif
}
else
{
#if defined(SVN_HAVE_GPG_AGENT)
if (strcmp(provider_name, "gpg_agent") == 0 &&
strcmp(provider_type, "simple") == 0)
{
svn_auth__get_gpg_agent_simple_provider(provider, pool);
}
#endif
#ifdef SVN_HAVE_KEYCHAIN_SERVICES
if (strcmp(provider_name, "keychain") == 0 &&
strcmp(provider_type, "simple") == 0)
{
svn_auth__get_keychain_simple_provider(provider, pool);
}
else if (strcmp(provider_name, "keychain") == 0 &&
strcmp(provider_type, "ssl_client_cert_pw") == 0)
{
svn_auth__get_keychain_ssl_client_cert_pw_provider(provider, pool);
}
#endif
#if defined(WIN32) && !defined(__MINGW32__)
if (strcmp(provider_name, "windows") == 0 &&
strcmp(provider_type, "simple") == 0)
{
svn_auth__get_windows_simple_provider(provider, pool);
}
else if (strcmp(provider_name, "windows") == 0 &&
strcmp(provider_type, "ssl_client_cert_pw") == 0)
{
svn_auth__get_windows_ssl_client_cert_pw_provider(provider, pool);
}
else if (strcmp(provider_name, "windows") == 0 &&
strcmp(provider_type, "ssl_server_trust") == 0)
{
svn_auth__get_windows_ssl_server_trust_provider(provider, pool);
}
else if (strcmp(provider_name, "windows") == 0 &&
strcmp(provider_type, "ssl_server_authority") == 0)
{
svn_auth__get_windows_ssl_server_authority_provider(provider, pool);
}
#endif
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_auth_get_platform_specific_client_providers(apr_array_header_t **providers,
svn_config_t *config,
apr_pool_t *pool)
{
svn_auth_provider_object_t *provider;
const char *password_stores_config_option;
apr_array_header_t *password_stores;
int i;
#define SVN__MAYBE_ADD_PROVIDER(list, p) \
{ if (p) APR_ARRAY_PUSH(list, svn_auth_provider_object_t *) = p; }
#define SVN__DEFAULT_AUTH_PROVIDER_LIST \
"gnome-keyring,kwallet,keychain,gpg-agent,windows-cryptoapi"
*providers = apr_array_make(pool, 12, sizeof(svn_auth_provider_object_t *));
svn_config_get(config,
&password_stores_config_option,
SVN_CONFIG_SECTION_AUTH,
SVN_CONFIG_OPTION_PASSWORD_STORES,
SVN__DEFAULT_AUTH_PROVIDER_LIST);
password_stores = svn_cstring_split(password_stores_config_option,
" ,", TRUE, pool);
for (i = 0; i < password_stores->nelts; i++)
{
const char *password_store = APR_ARRAY_IDX(password_stores, i,
const char *);
if (apr_strnatcmp(password_store, "gnome-keyring") == 0)
{
SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
"gnome_keyring",
"simple",
pool));
SVN__MAYBE_ADD_PROVIDER(*providers, provider);
SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
"gnome_keyring",
"ssl_client_cert_pw",
pool));
SVN__MAYBE_ADD_PROVIDER(*providers, provider);
}
else if (apr_strnatcmp(password_store, "gpg-agent") == 0)
{
SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
"gpg_agent",
"simple",
pool));
SVN__MAYBE_ADD_PROVIDER(*providers, provider);
}
else if (apr_strnatcmp(password_store, "kwallet") == 0)
{
SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
"kwallet",
"simple",
pool));
SVN__MAYBE_ADD_PROVIDER(*providers, provider);
SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
"kwallet",
"ssl_client_cert_pw",
pool));
SVN__MAYBE_ADD_PROVIDER(*providers, provider);
}
else if (apr_strnatcmp(password_store, "keychain") == 0)
{
SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
"keychain",
"simple",
pool));
SVN__MAYBE_ADD_PROVIDER(*providers, provider);
SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
"keychain",
"ssl_client_cert_pw",
pool));
SVN__MAYBE_ADD_PROVIDER(*providers, provider);
}
else if (apr_strnatcmp(password_store, "windows-cryptoapi") == 0)
{
SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
"windows",
"simple",
pool));
SVN__MAYBE_ADD_PROVIDER(*providers, provider);
SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
"windows",
"ssl_client_cert_pw",
pool));
SVN__MAYBE_ADD_PROVIDER(*providers, provider);
}
}
SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
"windows",
"ssl_server_trust",
pool));
SVN__MAYBE_ADD_PROVIDER(*providers, provider);
SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
"windows",
"ssl_server_authority",
pool));
SVN__MAYBE_ADD_PROVIDER(*providers, provider);
return SVN_NO_ERROR;
}
svn_error_t *
svn_auth__make_session_auth(svn_auth_baton_t **session_auth_baton,
const svn_auth_baton_t *auth_baton,
apr_hash_t *config,
const char *server_name,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_boolean_t store_passwords = SVN_CONFIG_DEFAULT_OPTION_STORE_PASSWORDS;
svn_boolean_t store_auth_creds = SVN_CONFIG_DEFAULT_OPTION_STORE_AUTH_CREDS;
const char *store_plaintext_passwords
= SVN_CONFIG_DEFAULT_OPTION_STORE_PLAINTEXT_PASSWORDS;
svn_boolean_t store_pp = SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP;
const char *store_pp_plaintext
= SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT;
svn_config_t *servers = NULL;
const char *server_group = NULL;
struct svn_auth_baton_t *ab;
ab = apr_pmemdup(result_pool, auth_baton, sizeof(*ab));
ab->slave_parameters = apr_hash_make(result_pool);
if (svn_auth_get_parameter(ab, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS) != NULL)
store_passwords = FALSE;
if (svn_auth_get_parameter(ab, SVN_AUTH_PARAM_NO_AUTH_CACHE) != NULL)
store_auth_creds = FALSE;
if (config)
{
servers = svn_hash_gets(config, SVN_CONFIG_CATEGORY_SERVERS);
if (servers)
{
SVN_ERR(svn_config_get_bool
(servers, &store_passwords, SVN_CONFIG_SECTION_GLOBAL,
SVN_CONFIG_OPTION_STORE_PASSWORDS,
store_passwords));
SVN_ERR(svn_config_get_yes_no_ask
(servers, &store_plaintext_passwords, SVN_CONFIG_SECTION_GLOBAL,
SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS,
SVN_CONFIG_DEFAULT_OPTION_STORE_PLAINTEXT_PASSWORDS));
SVN_ERR(svn_config_get_bool
(servers, &store_pp, SVN_CONFIG_SECTION_GLOBAL,
SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP,
store_pp));
SVN_ERR(svn_config_get_yes_no_ask
(servers, &store_pp_plaintext,
SVN_CONFIG_SECTION_GLOBAL,
SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT,
SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT));
SVN_ERR(svn_config_get_bool
(servers, &store_auth_creds, SVN_CONFIG_SECTION_GLOBAL,
SVN_CONFIG_OPTION_STORE_AUTH_CREDS,
store_auth_creds));
server_group = svn_config_find_group(servers, server_name,
SVN_CONFIG_SECTION_GROUPS,
scratch_pool);
if (server_group)
{
SVN_ERR(svn_config_get_bool(servers, &store_auth_creds,
server_group,
SVN_CONFIG_OPTION_STORE_AUTH_CREDS,
store_auth_creds));
SVN_ERR(svn_config_get_bool(servers, &store_passwords,
server_group,
SVN_CONFIG_OPTION_STORE_PASSWORDS,
store_passwords));
SVN_ERR(svn_config_get_yes_no_ask
(servers, &store_plaintext_passwords, server_group,
SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS,
store_plaintext_passwords));
SVN_ERR(svn_config_get_bool
(servers, &store_pp,
server_group, SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP,
store_pp));
SVN_ERR(svn_config_get_yes_no_ask
(servers, &store_pp_plaintext, server_group,
SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT,
store_pp_plaintext));
}
}
}
if (! store_passwords)
svn_auth_set_parameter(ab, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, "");
svn_auth_set_parameter(ab,
SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS,
store_plaintext_passwords);
if (! store_pp)
svn_auth_set_parameter(ab,
SVN_AUTH_PARAM_DONT_STORE_SSL_CLIENT_CERT_PP,
"");
svn_auth_set_parameter(ab,
SVN_AUTH_PARAM_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT,
store_pp_plaintext);
if (! store_auth_creds)
svn_auth_set_parameter(ab, SVN_AUTH_PARAM_NO_AUTH_CACHE, "");
if (server_group)
svn_auth_set_parameter(ab,
SVN_AUTH_PARAM_SERVER_GROUP,
apr_pstrdup(ab->pool, server_group));
*session_auth_baton = ab;
return SVN_NO_ERROR;
}
static svn_error_t *
dummy_first_creds(void **credentials,
void **iter_baton,
void *provider_baton,
apr_hash_t *parameters,
const char *realmstring,
apr_pool_t *pool)
{
*credentials = NULL;
*iter_baton = NULL;
return SVN_NO_ERROR;
}
void
svn_auth__get_dummmy_simple_provider(svn_auth_provider_object_t **provider,
apr_pool_t *pool)
{
static const svn_auth_provider_t vtable = {
SVN_AUTH_CRED_SIMPLE,
dummy_first_creds,
NULL, NULL
};
svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
po->vtable = &vtable;
*provider = po;
}