session.c
来自「linux subdivision ying gai ke yi le ba」· C语言 代码 · 共 888 行 · 第 1/2 页
C
888 行
/*
* session.c : routines for maintaining sessions state (to the DAV server)
*
* ====================================================================
* Copyright (c) 2000-2004 CollabNet. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://subversion.tigris.org/license-1.html.
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
*
* This software consists of voluntary contributions made by many
* individuals. For exact contribution history, see the revision
* history and logs, available at http://subversion.tigris.org/.
* ====================================================================
*/
#include <assert.h>
#include <apr_pools.h>
#define APR_WANT_STRFUNC
#include <apr_want.h>
#include <apr_general.h>
#include <ne_socket.h>
#include <ne_request.h>
#include <ne_uri.h>
#include <ne_auth.h>
#include "svn_error.h"
#include "svn_ra.h"
#include "svn_config.h"
#include "svn_delta.h"
#include "svn_version.h"
#include "svn_path.h"
#include "svn_private_config.h"
#include "ra_dav.h"
#define DEFAULT_HTTP_TIMEOUT 3600
/* a cleanup routine attached to the pool that contains the RA session
baton. */
static apr_status_t cleanup_session(void *sess)
{
ne_session_destroy(sess);
return APR_SUCCESS;
}
/* a cleanup routine attached to the pool that contains the RA session
root URI. */
static apr_status_t cleanup_uri(void *uri)
{
ne_uri_free(uri);
return APR_SUCCESS;
}
/* A neon-session callback to 'pull' authentication data when
challenged. In turn, this routine 'pulls' the data from the client
callbacks if needed. */
static int request_auth(void *userdata, const char *realm, int attempt,
char *username, char *password)
{
svn_error_t *err;
svn_ra_session_t *ras = userdata;
void *creds;
svn_auth_cred_simple_t *simple_creds;
/* No auth_baton? Give up. */
if (! ras->callbacks->auth_baton)
return -1;
if (attempt == 0)
{
const char *realmstring;
/* <https://svn.collab.net:80> Subversion repository */
realmstring = apr_psprintf (ras->pool, "<%s://%s:%d> %s",
ras->root.scheme, ras->root.host,
ras->root.port, realm);
err = svn_auth_first_credentials (&creds,
&(ras->auth_iterstate),
SVN_AUTH_CRED_SIMPLE,
realmstring,
ras->callbacks->auth_baton,
ras->pool);
}
else /* attempt > 0 */
/* ### TODO: if the http realm changed this time around, we
should be calling first_creds(), not next_creds(). */
err = svn_auth_next_credentials (&creds,
ras->auth_iterstate,
ras->pool);
if (err || ! creds)
{
svn_error_clear (err);
return -1;
}
simple_creds = creds;
/* ### silently truncates username/password to 256 chars. */
apr_cpystrn(username, simple_creds->username, NE_ABUFSIZ);
apr_cpystrn(password, simple_creds->password, NE_ABUFSIZ);
return 0;
}
static const apr_uint32_t neon_failure_map[][2] =
{
{ NE_SSL_NOTYETVALID, SVN_AUTH_SSL_NOTYETVALID },
{ NE_SSL_EXPIRED, SVN_AUTH_SSL_EXPIRED },
{ NE_SSL_IDMISMATCH, SVN_AUTH_SSL_CNMISMATCH },
{ NE_SSL_UNTRUSTED, SVN_AUTH_SSL_UNKNOWNCA }
};
/* Convert neon's SSL failure mask to our own failure mask. */
static apr_uint32_t
convert_neon_failures(int neon_failures)
{
apr_uint32_t svn_failures = 0;
apr_size_t i;
for (i = 0; i < sizeof(neon_failure_map) / (2 * sizeof(int)); ++i)
{
if (neon_failures & neon_failure_map[i][0])
{
svn_failures |= neon_failure_map[i][1];
neon_failures &= ~neon_failure_map[i][0];
}
}
/* Map any remaining neon failure bits to our OTHER bit. */
if (neon_failures)
{
svn_failures |= SVN_AUTH_SSL_OTHER;
}
return svn_failures;
}
/* A neon-session callback to validate the SSL certificate when the CA
is unknown or there are other SSL certificate problems. */
static int
server_ssl_callback(void *userdata,
int failures,
const ne_ssl_certificate *cert)
{
svn_ra_session_t *ras = userdata;
svn_auth_cred_ssl_server_trust_t *server_creds = NULL;
void *creds;
svn_auth_iterstate_t *state;
apr_pool_t *pool;
svn_error_t *error;
char *ascii_cert = ne_ssl_cert_export(cert);
char *issuer_dname = ne_ssl_readable_dname(ne_ssl_cert_issuer(cert));
svn_auth_ssl_server_cert_info_t cert_info;
char fingerprint[NE_SSL_DIGESTLEN];
char valid_from[NE_SSL_VDATELEN], valid_until[NE_SSL_VDATELEN];
const char *realmstring;
apr_uint32_t *svn_failures = apr_palloc (ras->pool, sizeof(*svn_failures));
/* Construct the realmstring, e.g. https://svn.collab.net:80 */
realmstring = apr_psprintf (ras->pool, "%s://%s:%d", ras->root.scheme,
ras->root.host, ras->root.port);
*svn_failures = convert_neon_failures(failures);
svn_auth_set_parameter(ras->callbacks->auth_baton,
SVN_AUTH_PARAM_SSL_SERVER_FAILURES,
svn_failures);
/* Extract the info from the certificate */
cert_info.hostname = ne_ssl_cert_identity(cert);
if (ne_ssl_cert_digest(cert, fingerprint) != 0)
{
strcpy(fingerprint, "<unknown>");
}
cert_info.fingerprint = fingerprint;
ne_ssl_cert_validity(cert, valid_from, valid_until);
cert_info.valid_from = valid_from;
cert_info.valid_until = valid_until;
cert_info.issuer_dname = issuer_dname;
cert_info.ascii_cert = ascii_cert;
svn_auth_set_parameter(ras->callbacks->auth_baton,
SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO,
&cert_info);
apr_pool_create(&pool, ras->pool);
error = svn_auth_first_credentials(&creds, &state,
SVN_AUTH_CRED_SSL_SERVER_TRUST,
realmstring,
ras->callbacks->auth_baton,
pool);
if (error || ! creds)
{
svn_error_clear(error);
}
else
{
server_creds = creds;
error = svn_auth_save_credentials(state, pool);
if (error)
{
/* It would be nice to show the error to the user somehow... */
svn_error_clear(error);
}
}
free(issuer_dname);
free(ascii_cert);
svn_auth_set_parameter(ras->callbacks->auth_baton,
SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, NULL);
apr_pool_destroy(pool);
return ! server_creds;
}
static svn_boolean_t
client_ssl_decrypt_cert(svn_ra_session_t *ras,
const char *cert_file,
ne_ssl_client_cert *clicert)
{
svn_auth_iterstate_t *state;
svn_error_t *error;
apr_pool_t *pool;
svn_boolean_t ok = FALSE;
void *creds;
int try;
apr_pool_create(&pool, ras->pool);
for (try = 0; TRUE; ++try)
{
if (try == 0)
{
error = svn_auth_first_credentials(&creds, &state,
SVN_AUTH_CRED_SSL_CLIENT_CERT_PW,
cert_file,
ras->callbacks->auth_baton,
pool);
}
else
{
error = svn_auth_next_credentials(&creds, state, pool);
}
if (error || ! creds)
{
/* Failure or too many attempts */
svn_error_clear(error);
break;
}
else
{
svn_auth_cred_ssl_client_cert_pw_t *pw_creds = creds;
if (ne_ssl_clicert_decrypt(clicert, pw_creds->password) == 0)
{
/* Success */
ok = TRUE;
break;
}
}
}
apr_pool_destroy(pool);
return ok;
}
static void
client_ssl_callback(void *userdata, ne_session *sess,
const ne_ssl_dname *const *dnames,
int dncount)
{
svn_ra_session_t *ras = userdata;
ne_ssl_client_cert *clicert = NULL;
void *creds;
svn_auth_iterstate_t *state;
const char *realmstring;
apr_pool_t *pool;
svn_error_t *error;
int try;
apr_pool_create(&pool, ras->pool);
realmstring = apr_psprintf (pool, "%s://%s:%d", ras->root.scheme,
ras->root.host, ras->root.port);
for (try = 0; TRUE; ++try)
{
if (try == 0)
{
error = svn_auth_first_credentials(&creds, &state,
SVN_AUTH_CRED_SSL_CLIENT_CERT,
realmstring,
ras->callbacks->auth_baton,
pool);
}
else
{
error = svn_auth_next_credentials(&creds, state, pool);
}
if (error || ! creds)
{
/* Failure or too many attempts */
svn_error_clear(error);
break;
}
else
{
svn_auth_cred_ssl_client_cert_t *client_creds = creds;
clicert = ne_ssl_clicert_read(client_creds->cert_file);
if (clicert)
{
if (! ne_ssl_clicert_encrypted(clicert) ||
client_ssl_decrypt_cert(ras, client_creds->cert_file,
clicert))
{
ne_ssl_set_clicert(sess, clicert);
}
break;
}
}
}
apr_pool_destroy(pool);
}
/* Set *PROXY_HOST, *PROXY_PORT, *PROXY_USERNAME, *PROXY_PASSWORD,
* *TIMEOUT_SECONDS and *NEON_DEBUG to the information for REQUESTED_HOST,
* allocated in POOL, if there is any applicable information. If there is
* no applicable information or if there is an error, then set *PROXY_PORT
* to (unsigned int) -1, *TIMEOUT_SECONDS and *NEON_DEBUG to zero, and the
* rest to NULL. This function can return an error, so before checking any
* values, check the error return value.
*/
static svn_error_t *get_server_settings(const char **proxy_host,
unsigned int *proxy_port,
const char **proxy_username,
const char **proxy_password,
int *timeout_seconds,
int *neon_debug,
svn_boolean_t *compression,
svn_config_t *cfg,
const char *requested_host,
apr_pool_t *pool)
{
const char *exceptions, *port_str, *timeout_str, *server_group;
const char *debug_str;
svn_boolean_t is_exception = FALSE;
/* If we find nothing, default to nulls. */
*proxy_host = NULL;
*proxy_port = (unsigned int) -1;
*proxy_username = NULL;
*proxy_password = NULL;
port_str = NULL;
timeout_str = NULL;
debug_str = NULL;
/* If there are defaults, use them, but only if the requested host
is not one of the exceptions to the defaults. */
svn_config_get(cfg, &exceptions, SVN_CONFIG_SECTION_GLOBAL,
SVN_CONFIG_OPTION_HTTP_PROXY_EXCEPTIONS, NULL);
if (exceptions)
{
apr_array_header_t *l = svn_cstring_split (exceptions, ",", TRUE, pool);
is_exception = svn_cstring_match_glob_list (requested_host, l);
}
if (! is_exception)
{
svn_config_get(cfg, proxy_host, SVN_CONFIG_SECTION_GLOBAL,
SVN_CONFIG_OPTION_HTTP_PROXY_HOST, NULL);
svn_config_get(cfg, &port_str, SVN_CONFIG_SECTION_GLOBAL,
SVN_CONFIG_OPTION_HTTP_PROXY_PORT, NULL);
svn_config_get(cfg, proxy_username, SVN_CONFIG_SECTION_GLOBAL,
SVN_CONFIG_OPTION_HTTP_PROXY_USERNAME, NULL);
svn_config_get(cfg, proxy_password, SVN_CONFIG_SECTION_GLOBAL,
SVN_CONFIG_OPTION_HTTP_PROXY_PASSWORD, NULL);
svn_config_get(cfg, &timeout_str, SVN_CONFIG_SECTION_GLOBAL,
SVN_CONFIG_OPTION_HTTP_TIMEOUT, NULL);
SVN_ERR(svn_config_get_bool(cfg, compression, SVN_CONFIG_SECTION_GLOBAL,
SVN_CONFIG_OPTION_HTTP_COMPRESSION, TRUE));
svn_config_get(cfg, &debug_str, SVN_CONFIG_SECTION_GLOBAL,
SVN_CONFIG_OPTION_NEON_DEBUG_MASK, NULL);
}
if (cfg)
server_group = svn_config_find_group(cfg, requested_host,
SVN_CONFIG_SECTION_GROUPS, pool);
else
server_group = NULL;
if (server_group)
{
svn_config_get(cfg, proxy_host, server_group,
SVN_CONFIG_OPTION_HTTP_PROXY_HOST, *proxy_host);
svn_config_get(cfg, &port_str, server_group,
SVN_CONFIG_OPTION_HTTP_PROXY_PORT, port_str);
svn_config_get(cfg, proxy_username, server_group,
SVN_CONFIG_OPTION_HTTP_PROXY_USERNAME, *proxy_username);
svn_config_get(cfg, proxy_password, server_group,
SVN_CONFIG_OPTION_HTTP_PROXY_PASSWORD, *proxy_password);
svn_config_get(cfg, &timeout_str, server_group,
SVN_CONFIG_OPTION_HTTP_TIMEOUT, timeout_str);
SVN_ERR(svn_config_get_bool(cfg, compression, server_group,
SVN_CONFIG_OPTION_HTTP_COMPRESSION,
*compression));
svn_config_get(cfg, &debug_str, server_group,
SVN_CONFIG_OPTION_NEON_DEBUG_MASK, debug_str);
}
/* Special case: convert the port value, if any. */
if (port_str)
{
char *endstr;
const long int port = strtol(port_str, &endstr, 10);
if (*endstr)
return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, NULL,
_("Invalid URL: illegal character in proxy "
"port number"));
if (port < 0)
return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, NULL,
_("Invalid URL: negative proxy port number"));
if (port > 65535)
return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, NULL,
_("Invalid URL: proxy port number greater "
"than maximum TCP port number 65535"));
*proxy_port = port;
}
else
*proxy_port = 80;
if (timeout_str)
{
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?