#include <serf.h>
#include <apr_base64.h>
#include "ra_serf.h"
#include "win32_auth_sspi.h"
#include "svn_private_config.h"
static svn_error_t *
handle_basic_auth(svn_ra_serf__session_t *session,
svn_ra_serf__connection_t *conn,
serf_request_t *request,
serf_bucket_t *response,
char *auth_hdr,
char *auth_attr,
apr_pool_t *pool);
static svn_error_t *
init_basic_connection(svn_ra_serf__session_t *session,
svn_ra_serf__connection_t *conn,
apr_pool_t *pool);
static svn_error_t *
setup_request_basic_auth(svn_ra_serf__connection_t *conn,
serf_bucket_t *hdrs_bkt);
static svn_error_t *
handle_proxy_basic_auth(svn_ra_serf__session_t *session,
svn_ra_serf__connection_t *conn,
serf_request_t *request,
serf_bucket_t *response,
char *auth_hdr,
char *auth_attr,
apr_pool_t *pool);
static svn_error_t *
init_proxy_basic_connection(svn_ra_serf__session_t *session,
svn_ra_serf__connection_t *conn,
apr_pool_t *pool);
static svn_error_t *
setup_request_proxy_basic_auth(svn_ra_serf__connection_t *conn,
serf_bucket_t *hdrs_bkt);
static const svn_ra_serf__auth_protocol_t serf_auth_protocols[] = {
{
401,
"Basic",
init_basic_connection,
handle_basic_auth,
setup_request_basic_auth,
},
{
407,
"Basic",
init_proxy_basic_connection,
handle_proxy_basic_auth,
setup_request_proxy_basic_auth,
},
#ifdef SVN_RA_SERF_SSPI_ENABLED
{
401,
"NTLM",
init_sspi_connection,
handle_sspi_auth,
setup_request_sspi_auth,
},
{
407,
"NTLM",
init_proxy_sspi_connection,
handle_proxy_sspi_auth,
setup_request_proxy_sspi_auth,
},
#endif
{ 0 }
};
void
svn_ra_serf__encode_auth_header(const char * protocol, char **header,
const char * data, apr_size_t data_len,
apr_pool_t *pool)
{
apr_size_t encoded_len, proto_len;
char * ptr;
encoded_len = apr_base64_encode_len(data_len);
proto_len = strlen(protocol);
*header = apr_palloc(pool, encoded_len + proto_len + 1);
ptr = *header;
apr_cpystrn(ptr, protocol, proto_len + 1);
ptr += proto_len;
*ptr++ = ' ';
apr_base64_encode(ptr, data, data_len);
}
svn_error_t *
svn_ra_serf__handle_auth(int code,
svn_ra_serf__session_t *session,
svn_ra_serf__connection_t *conn,
serf_request_t *request,
serf_bucket_t *response,
apr_pool_t *pool)
{
serf_bucket_t *hdrs;
const svn_ra_serf__auth_protocol_t *prot = NULL;
char *auth_name = NULL, *auth_attr, *auth_hdr=NULL, *header, *header_attr;
svn_error_t *cached_err = SVN_NO_ERROR;
hdrs = serf_bucket_response_get_headers(response);
if (code == 401)
auth_hdr = (char*)serf_bucket_headers_get(hdrs, "WWW-Authenticate");
else if (code == 407)
auth_hdr = (char*)serf_bucket_headers_get(hdrs, "Proxy-Authenticate");
if (!auth_hdr)
{
if (session->auth_protocol)
return svn_error_createf(SVN_ERR_AUTHN_FAILED, NULL,
"%s Authentication failed",
session->auth_protocol->auth_name);
else
return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL, NULL);
}
header = apr_strtok(auth_hdr, ",", &header_attr);
while (header)
{
svn_boolean_t proto_found = FALSE;
auth_name = apr_strtok(header, " ", &auth_attr);
cached_err = SVN_NO_ERROR;
for (prot = serf_auth_protocols; prot->code != 0; ++prot)
{
if (code == prot->code && strcasecmp(auth_name, prot->auth_name) == 0)
{
svn_serf__auth_handler_func_t handler = prot->handle_func;
svn_error_t *err = NULL;
if (code == 401 && session->auth_protocol != prot)
{
err = prot->init_conn_func(session, conn, session->pool);
if (err == SVN_NO_ERROR)
session->auth_protocol = prot;
else
session->auth_protocol = NULL;
}
else if (code == 407 && session->proxy_auth_protocol != prot)
{
err = prot->init_conn_func(session, conn, session->pool);
if (err == SVN_NO_ERROR)
session->proxy_auth_protocol = prot;
else
session->proxy_auth_protocol = NULL;
}
if (err == SVN_NO_ERROR)
{
proto_found = TRUE;
err = handler(session, conn, request, response,
header, auth_attr, session->pool);
}
if (err)
{
proto_found = FALSE;
prot = NULL;
if (cached_err)
svn_error_clear(cached_err);
cached_err = err;
}
break;
}
}
if (proto_found)
break;
header = apr_strtok(NULL, ",", &header_attr);
}
SVN_ERR(cached_err);
if (!prot || prot->auth_name == NULL)
{
return svn_error_createf(SVN_ERR_AUTHN_FAILED, NULL,
"%s authentication not supported.\n"
"Authentication failed", auth_name);
}
return SVN_NO_ERROR;
}
static svn_error_t *
handle_basic_auth(svn_ra_serf__session_t *session,
svn_ra_serf__connection_t *conn,
serf_request_t *request,
serf_bucket_t *response,
char *auth_hdr,
char *auth_attr,
apr_pool_t *pool)
{
void *creds;
char *last, *realm_name;
svn_auth_cred_simple_t *simple_creds;
const char *tmp;
apr_size_t tmp_len;
apr_port_t port;
int i;
if (!session->realm)
{
char *attr;
attr = apr_strtok(auth_attr, "=", &last);
if (strcasecmp(attr, "realm") == 0)
{
realm_name = apr_strtok(NULL, "=", &last);
if (realm_name[0] == '\"')
{
apr_size_t realm_len;
realm_len = strlen(realm_name);
if (realm_name[realm_len - 1] == '\"')
{
realm_name[realm_len - 1] = '\0';
realm_name++;
}
}
}
else
{
return svn_error_create
(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
_("Missing 'realm' attribute in Authorization header"));
}
if (!realm_name)
{
return svn_error_create
(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
_("Missing 'realm' attribute in Authorization header"));
}
if (session->repos_url.port_str)
{
port = session->repos_url.port;
}
else
{
port = apr_uri_port_of_scheme(session->repos_url.scheme);
}
session->realm = apr_psprintf(session->pool, "<%s://%s:%d> %s",
session->repos_url.scheme,
session->repos_url.hostname,
port,
realm_name);
}
if (!session->auth_state)
{
SVN_ERR(svn_auth_first_credentials(&creds,
&session->auth_state,
SVN_AUTH_CRED_SIMPLE,
session->realm,
session->wc_callbacks->auth_baton,
session->pool));
}
else
{
SVN_ERR(svn_auth_next_credentials(&creds,
session->auth_state,
session->pool));
}
session->auth_attempts++;
if (!creds || session->auth_attempts > 4)
{
return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL,
"No more credentials or we tried too many times.\n"
"Authentication failed");
}
simple_creds = creds;
tmp = apr_pstrcat(session->pool,
simple_creds->username, ":", simple_creds->password, NULL);
tmp_len = strlen(tmp);
svn_ra_serf__encode_auth_header(session->auth_protocol->auth_name,
&session->auth_value, tmp, tmp_len, pool);
session->auth_header = "Authorization";
for (i = 0; i < session->num_conns; i++)
{
session->conns[i]->auth_header = session->auth_header;
session->conns[i]->auth_value = session->auth_value;
}
return SVN_NO_ERROR;
}
static svn_error_t *
init_basic_connection(svn_ra_serf__session_t *session,
svn_ra_serf__connection_t *conn,
apr_pool_t *pool)
{
conn->auth_header = session->auth_header;
conn->auth_value = session->auth_value;
return SVN_NO_ERROR;
}
static svn_error_t *
setup_request_basic_auth(svn_ra_serf__connection_t *conn,
serf_bucket_t *hdrs_bkt)
{
if (conn->auth_header && conn->auth_value)
{
serf_bucket_headers_setn(hdrs_bkt, conn->auth_header, conn->auth_value);
}
return SVN_NO_ERROR;
}
static svn_error_t *
handle_proxy_basic_auth(svn_ra_serf__session_t *session,
svn_ra_serf__connection_t *conn,
serf_request_t *request,
serf_bucket_t *response,
char *auth_hdr,
char *auth_attr,
apr_pool_t *pool)
{
const char *tmp;
apr_size_t tmp_len;
int i;
tmp = apr_pstrcat(session->pool,
session->proxy_username, ":",
session->proxy_password, NULL);
tmp_len = strlen(tmp);
session->proxy_auth_attempts++;
if (session->proxy_auth_attempts > 1)
{
return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL,
"Proxy authentication failed");
}
svn_ra_serf__encode_auth_header(session->proxy_auth_protocol->auth_name,
&session->proxy_auth_value,
tmp, tmp_len, pool);
session->proxy_auth_header = "Proxy-Authorization";
for (i = 0; i < session->num_conns; i++)
{
session->conns[i]->proxy_auth_header = session->proxy_auth_header;
session->conns[i]->proxy_auth_value = session->proxy_auth_value;
}
return SVN_NO_ERROR;
}
static svn_error_t *
init_proxy_basic_connection(svn_ra_serf__session_t *session,
svn_ra_serf__connection_t *conn,
apr_pool_t *pool)
{
conn->proxy_auth_header = session->proxy_auth_header;
conn->proxy_auth_value = session->proxy_auth_value;
return SVN_NO_ERROR;
}
static svn_error_t *
setup_request_proxy_basic_auth(svn_ra_serf__connection_t *conn,
serf_bucket_t *hdrs_bkt)
{
if (conn->proxy_auth_header && conn->proxy_auth_value)
{
serf_bucket_headers_setn(hdrs_bkt, conn->proxy_auth_header,
conn->proxy_auth_value);
}
return SVN_NO_ERROR;
}