#include <apr_pools.h>
#include <apr_tables.h>
#include <apr_strings.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"
typedef struct
{
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 *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;
};
void
svn_auth_open(svn_auth_baton_t **auth_baton,
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 = apr_hash_get(ab->tables,
provider->vtable->cred_kind, APR_HASH_KEY_STRING);
if (! table)
{
table = apr_pcalloc(pool, sizeof(*table));
table->providers
= apr_array_make(pool, 1, sizeof(svn_auth_provider_object_t *));
apr_hash_set(ab->tables,
provider->vtable->cred_kind, APR_HASH_KEY_STRING,
table);
}
APR_ARRAY_PUSH(table->providers, svn_auth_provider_object_t *)
= provider;
}
*auth_baton = ab;
}
void
svn_auth_set_parameter(svn_auth_baton_t *auth_baton,
const char *name,
const void *value)
{
apr_hash_set(auth_baton->parameters, name, APR_HASH_KEY_STRING, value);
}
const void *
svn_auth_get_parameter(svn_auth_baton_t *auth_baton,
const char *name)
{
return apr_hash_get(auth_baton->parameters, name, APR_HASH_KEY_STRING);
}
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;
table = apr_hash_get(auth_baton->tables, cred_kind, APR_HASH_KEY_STRING);
if (! table)
return svn_error_createf(SVN_ERR_AUTHN_NO_PROVIDER, NULL,
"No provider registered for '%s' credentials",
cred_kind);
cache_key = apr_pstrcat(pool, cred_kind, ":", realmstring, NULL);
creds = apr_hash_get(auth_baton->creds_cache,
cache_key, APR_HASH_KEY_STRING);
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,
auth_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;
*state = iterstate;
apr_hash_set(auth_baton->creds_cache,
apr_pstrdup(auth_baton->pool, cache_key),
APR_HASH_KEY_STRING,
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, auth_baton->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, auth_baton->parameters,
state->realmstring, auth_baton->pool));
}
if (creds != NULL)
{
apr_hash_set(auth_baton->creds_cache,
state->cache_key, APR_HASH_KEY_STRING,
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;
svn_auth_baton_t *auth_baton;
void *creds;
if (! state || state->table->providers->nelts <= state->provider_idx)
return SVN_NO_ERROR;
auth_baton = state->auth_baton;
creds = apr_hash_get(state->auth_baton->creds_cache,
state->cache_key, APR_HASH_KEY_STRING);
if (! creds)
return SVN_NO_ERROR;
no_auth_cache = apr_hash_get(auth_baton->parameters,
SVN_AUTH_PARAM_NO_AUTH_CACHE,
APR_HASH_KEY_STRING);
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,
auth_baton->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,
auth_baton->parameters,
state->realmstring,
pool));
if (save_succeeded)
break;
}
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)
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.0",
provider_name,
SVN_VER_MAJOR);
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 = (svn_version_func_t) version_function_symbol;
const svn_version_checklist_t check_list[] =
{
{ library_label, version_function },
{ NULL, NULL }
};
SVN_ERR(svn_ver_check_list(svn_subr_version(), check_list));
}
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 =
(svn_auth_simple_provider_func_t)
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 =
(svn_auth_ssl_client_cert_pw_provider_func_t)
provider_function_symbol;
provider_function(provider, pool);
}
}
}
#endif
}
else
{
#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);
}
#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;
if (config)
{
svn_config_get(config,
&password_stores_config_option,
SVN_CONFIG_SECTION_AUTH,
SVN_CONFIG_OPTION_PASSWORD_STORES,
"gnome-keyring,kwallet,keychain,windows-cryptoapi");
}
else
{
password_stores_config_option = "gnome-keyring,kwallet,keychain,windows-cryptoapi";
}
*providers = apr_array_make(pool, 12, sizeof(svn_auth_provider_object_t *));
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));
if (provider)
APR_ARRAY_PUSH(*providers, svn_auth_provider_object_t *) = provider;
SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
"gnome_keyring",
"ssl_client_cert_pw",
pool));
if (provider)
APR_ARRAY_PUSH(*providers, svn_auth_provider_object_t *) = provider;
continue;
}
if (apr_strnatcmp(password_store, "kwallet") == 0)
{
SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
"kwallet",
"simple",
pool));
if (provider)
APR_ARRAY_PUSH(*providers, svn_auth_provider_object_t *) = provider;
SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
"kwallet",
"ssl_client_cert_pw",
pool));
if (provider)
APR_ARRAY_PUSH(*providers, svn_auth_provider_object_t *) = provider;
continue;
}
if (apr_strnatcmp(password_store, "keychain") == 0)
{
SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
"keychain",
"simple",
pool));
if (provider)
APR_ARRAY_PUSH(*providers, svn_auth_provider_object_t *) = provider;
SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
"keychain",
"ssl_client_cert_pw",
pool));
if (provider)
APR_ARRAY_PUSH(*providers, svn_auth_provider_object_t *) = provider;
continue;
}
if (apr_strnatcmp(password_store, "windows-cryptoapi") == 0)
{
SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
"windows",
"simple",
pool));
if (provider)
APR_ARRAY_PUSH(*providers, svn_auth_provider_object_t *) = provider;
SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
"windows",
"ssl_client_cert_pw",
pool));
if (provider)
APR_ARRAY_PUSH(*providers, svn_auth_provider_object_t *) = provider;
continue;
}
return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
_("Invalid config: unknown password store "
"'%s'"),
password_store);
}
return SVN_NO_ERROR;
}