blob: 3b7ad618f9726e899d36f503229cddfeb6e5264a [file]
/* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <limits.h>
#include <apr.h>
#include <apr_base64.h>
#include <apr_errno.h>
#include <apr_strings.h>
#include <apr_lib.h>
#if APR_HAS_THREADS
# include <apr_thread_mutex.h>
#endif
#include "serf.h"
#include "serf_private.h"
#include "auth.h"
/* These authentication schemes are in order of decreasing security, the topmost
scheme will be used first when the server supports it.
Each set of handlers should support both server (401) and proxy (407)
authentication.
Use lower case for the scheme names to enable case insensitive matching.
The size of the array is the number of bits in serf__authn_scheme_t::type,
plus one slot for the NULL sentinel.
*/
#define AUTHN_SCHEMES_SIZE (sizeof(unsigned int) * CHAR_BIT)
static const serf__authn_scheme_t *serf_authn_schemes[AUTHN_SCHEMES_SIZE + 1] = {
#ifdef SERF_HAVE_SPNEGO
&serf__spnego_authn_scheme,
#ifdef WIN32
&serf__ntlm_authn_scheme,
#endif /* #ifdef WIN32 */
#endif /* SERF_HAVE_SPNEGO */
&serf__digest_authn_scheme,
&serf__basic_authn_scheme,
/* ADD NEW AUTHENTICATION IMPLEMENTATIONS HERE (as they're written) */
/* sentinel */
NULL
/* The rest of the array will be automagically zero-initialized. */
};
#if APR_HAS_THREADS
/* Guard access to serf_authn_schemes and related global data. */
static apr_thread_mutex_t *authn_schemes_guard;
static apr_pool_t *authn_schemes_guard_pool;
static apr_status_t init_authn_schemes_guard(serf_config_t *config);
#endif
static apr_status_t lock_authn_schemes(serf_config_t *config)
{
#if APR_HAS_THREADS
apr_status_t status = init_authn_schemes_guard(config);
if (status == APR_SUCCESS) {
status = apr_thread_mutex_lock(authn_schemes_guard);
if (status) {
char buffer[256];
serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, config,
"Lock authn schemes: %s\n",
apr_strerror(status, buffer, sizeof(buffer)));
}
}
return status;
#else
return APR_SUCCESS;
#endif
}
static apr_status_t unlock_authn_schemes(serf_config_t *config)
{
#if APR_HAS_THREADS
apr_status_t status = init_authn_schemes_guard(config);
if (status == APR_SUCCESS) {
status = apr_thread_mutex_unlock(authn_schemes_guard);
if (status) {
char buffer[256];
serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, config,
"Unlock authn schemes: %s\n",
apr_strerror(status, buffer, sizeof(buffer)));
}
}
return status;
#else
return APR_SUCCESS;
#endif
}
/* Reads and discards all bytes in the response body. */
static apr_status_t discard_body(serf_bucket_t *response)
{
apr_status_t status;
const char *data;
apr_size_t len;
while (1) {
status = serf_bucket_read(response, SERF_READ_ALL_AVAIL, &data, &len);
if (status) {
return status;
}
/* feed me */
}
}
/**
* handle_auth_header is called for each header in the response. It filters
* out the Authenticate headers (WWW or Proxy depending on what's needed) and
* tries to find a matching scheme handler.
*
* Returns a non-0 value of a matching handler was found.
*/
static int handle_auth_headers(int code,
apr_hash_t *hdrs,
serf_request_t *request,
serf_bucket_t *response,
apr_pool_t *pool)
{
int scheme_idx;
serf_connection_t *conn = request->conn;
serf_context_t *ctx = conn->ctx;
apr_status_t status, lock_status;
lock_status = lock_authn_schemes(conn->config);
if (lock_status)
return lock_status;
status = SERF_ERROR_AUTHN_NOT_SUPPORTED;
/* Find the matching authentication handler.
Note that we don't reuse the auth scheme stored in the context,
as that may have changed. (ex. fallback from ntlm to basic.) */
for (scheme_idx = 0; serf_authn_schemes[scheme_idx]; ++scheme_idx) {
const char *auth_hdr;
serf__auth_handler_func_t handler;
serf__authn_info_t *authn_info;
const serf__authn_scheme_t *scheme = serf_authn_schemes[scheme_idx];
if (! (ctx->authn_types & scheme->type))
continue;
serf__log(LOGLVL_INFO, LOGCOMP_AUTHN, __FILE__, conn->config,
"Client supports: %s\n", scheme->name);
auth_hdr = apr_hash_get(hdrs, scheme->key, APR_HASH_KEY_STRING);
if (!auth_hdr)
continue;
if (code == SERF_AUTHN_CODE_HOST) {
authn_info = serf__get_authn_info_for_server(conn);
} else {
authn_info = &ctx->proxy_authn_info;
}
if (authn_info->failed_authn_types & scheme->type) {
/* Skip this authn type since we already tried it before. */
continue;
}
/* Found a matching scheme */
status = APR_SUCCESS;
handler = scheme->handle_func;
serf__log(LOGLVL_INFO, LOGCOMP_AUTHN, __FILE__, conn->config,
"... matched: %s\n", scheme->name);
/* If this is the first time we use this scheme on this connection,
make sure to initialize the authentication handler first. */
if (authn_info->scheme != scheme) {
status = scheme->init_conn_func(scheme, code, conn,
conn->pool);
if (!status)
authn_info->scheme = scheme;
else
authn_info->scheme = NULL;
}
if (!status) {
const char *auth_attr = strchr(auth_hdr, ' ');
if (auth_attr) {
auth_attr++;
}
status = handler(scheme, code, request, response,
auth_hdr, auth_attr, ctx->pool);
}
if (status == APR_SUCCESS)
break;
/* No success authenticating with this scheme, try the next.
If no more authn schemes are found the status of this scheme will be
returned.
*/
serf__log(LOGLVL_INFO, LOGCOMP_AUTHN, __FILE__, conn->config,
"%s authentication failed.\n", scheme->name);
/* Clear per-request auth_baton when switching to next auth scheme. */
request->auth_baton = NULL;
/* Remember failed auth types to skip it in future. */
authn_info->failed_authn_types |= scheme->type;
}
lock_status = unlock_authn_schemes(conn->config);
if (lock_status)
return lock_status;
return status;
}
/**
* Baton passed to the store_header_in_dict callback function
*/
typedef struct auth_baton_t {
const char *header;
apr_pool_t *pool;
apr_hash_t *hdrs;
} auth_baton_t;
static int store_header_in_dict(void *baton,
const char *key,
const char *header)
{
auth_baton_t *ab = baton;
apr_size_t auth_attr_len;
char *auth_name;
/* We're only interested in xxxx-Authenticate headers. */
if (strcasecmp(key, ab->header) != 0)
return 0;
/* Extract the authentication scheme name. */
auth_attr_len = strcspn(header, " ");
auth_name = apr_pstrmemdup(ab->pool, header, auth_attr_len);
/* Convert scheme name to lower case to enable case insensitive matching. */
serf__tolower_inplace(auth_name, auth_attr_len);
apr_hash_set(ab->hdrs, auth_name, APR_HASH_KEY_STRING,
apr_pstrdup(ab->pool, header));
return 0;
}
/* Dispatch authentication handling. This function matches the possible
authentication mechanisms with those available. Server and proxy
authentication are evaluated separately. */
static apr_status_t dispatch_auth(int code,
serf_request_t *request,
serf_bucket_t *response,
apr_pool_t *pool)
{
if (code == SERF_AUTHN_CODE_HOST || code == SERF_AUTHN_CODE_PROXY) {
serf_bucket_t *hdrs;
auth_baton_t ab = { 0 };
ab.hdrs = apr_hash_make(pool);
ab.pool = pool;
if (code == SERF_AUTHN_CODE_HOST)
ab.header = "WWW-Authenticate";
else
ab.header = "Proxy-Authenticate";
hdrs = serf_bucket_response_get_headers(response);
#ifdef SERF_LOGGING_ENABLED
if (serf__log_enabled(LOGLVL_WARNING, LOGCOMP_AUTHN,
request->conn->config)) {
const char *auth_hdr;
/* ### headers_get() doesn't tell us whether to free this result
### or not. but... meh. debug mode. */
auth_hdr = serf_bucket_headers_get(hdrs, ab.header);
if (auth_hdr == NULL) {
serf__log(LOGLVL_WARNING, LOGCOMP_AUTHN, __FILE__,
request->conn->config,
"%s header missing in response!\n", ab.header);
} else {
serf__log(LOGLVL_DEBUG, LOGCOMP_AUTHN, __FILE__,
request->conn->config,
"%s authz required. Response header(s): %s\n",
code == SERF_AUTHN_CODE_HOST ? "Server" : "Proxy",
auth_hdr);
}
}
#endif /* SERF_LOGGING_ENABLED */
/* Store all WWW- or Proxy-Authenticate headers in a dictionary.
Note: it is possible to have multiple Authentication: headers. We do
not want to combine them (per normal header combination rules) as that
would make it hard to parse. Instead, we want to individually parse
and handle each header in the response, looking for one that we can
work with.
*/
serf_bucket_headers_do(hdrs,
store_header_in_dict,
&ab);
if (apr_hash_count(ab.hdrs) == 0)
return SERF_ERROR_AUTHN_FAILED;
/* Iterate over all authentication schemes, in order of decreasing
security. Try to find a authentication schema the server support. */
return handle_auth_headers(code, ab.hdrs,
request, response, pool);
}
return APR_SUCCESS;
}
/* Read the headers of the response and try the available handlers if
authentication or validation is needed.
*CONSUMED_RESPONSE will be 1 if authentication is involved (either a 401/407
response or a response with an authn header), 0 otherwise. */
apr_status_t serf__handle_auth_response(bool *consumed_response,
serf_request_t *request,
serf_bucket_t *response,
apr_pool_t *pool)
{
apr_status_t status;
serf_status_line sl;
*consumed_response = false;
/* TODO: the response bucket was created by the application, not at all
guaranteed that this is of type response_bucket!! */
status = serf_bucket_response_status(response, &sl);
if (SERF_BUCKET_READ_ERROR(status)) {
return status;
}
if (!sl.version && (APR_STATUS_IS_EOF(status) ||
APR_STATUS_IS_EAGAIN(status))) {
return status;
}
status = serf_bucket_response_wait_for_headers(response);
if (status) {
if (!APR_STATUS_IS_EOF(status)) {
return status;
}
/* If status is APR_EOF, there were no headers to read.
This can be ok in some situations, and it definitely
means there's no authentication requested now. */
return APR_SUCCESS;
}
if (sl.code == SERF_AUTHN_CODE_HOST || sl.code == SERF_AUTHN_CODE_PROXY) {
/* Authentication requested. */
/* Don't bother handling the authentication request if the response
wasn't received completely yet. Serf will call serf__handle_auth_response
again when more data is received. */
status = dispatch_auth(sl.code, request, response, pool);
if (status != APR_SUCCESS) {
return status;
}
request->auth_done = true;
/* Requeue the request with the necessary auth headers.*/
status = serf_connection__request_requeue(request);
if (status)
return status;
*consumed_response = true;
return APR_SUCCESS;
} else {
serf__validate_response_func_t validate_resp;
serf_connection_t *conn = request->conn;
serf_context_t *ctx = conn->ctx;
serf__authn_info_t *authn_info;
apr_status_t resp_status = APR_SUCCESS;
/* Validate the response server authn headers. */
authn_info = serf__get_authn_info_for_server(conn);
if (authn_info->scheme) {
validate_resp = authn_info->scheme->validate_response_func;
resp_status = validate_resp(authn_info->scheme, HOST, sl.code,
conn, request, response, pool);
}
/* Validate the response proxy authn headers. */
authn_info = &ctx->proxy_authn_info;
if (!resp_status && authn_info->scheme) {
validate_resp = authn_info->scheme->validate_response_func;
resp_status = validate_resp(authn_info->scheme, PROXY, sl.code,
conn, request, response, pool);
}
if (resp_status) {
/* If there was an error in the final step of the authentication,
consider the response body as invalid and discard it. */
status = discard_body(response);
*consumed_response = true;
if (!APR_STATUS_IS_EOF(status)) {
return status;
}
/* The whole body was discarded, now return our error. */
return resp_status;
}
}
request->auth_done = true;
return APR_SUCCESS;
}
/**
* base64 encode the authentication data and build an authentication
* header in this format:
* [SCHEME] [BASE64 of auth DATA]
*/
void serf__encode_auth_header(const char **header,
const char *scheme,
const char *data,
apr_size_t data_len,
apr_pool_t *pool)
{
apr_size_t encoded_len, scheme_len;
int int_data_len;
char *ptr;
SERF__POSITIVE_TO_INT(int_data_len, apr_size_t, data_len);
encoded_len = apr_base64_encode_len(int_data_len);
scheme_len = strlen(scheme);
ptr = apr_palloc(pool, encoded_len + scheme_len + 1);
*header = ptr;
apr_cpystrn(ptr, scheme, scheme_len + 1);
ptr += scheme_len;
*ptr++ = ' ';
apr_base64_encode(ptr, data, int_data_len);
}
const char *serf__construct_realm(peer_t peer,
serf_connection_t *conn,
const char *realm_name,
apr_pool_t *pool)
{
if (peer == HOST) {
return apr_psprintf(pool, "<%s://%s:%d> %s",
conn->host_info.scheme,
conn->host_info.hostname,
conn->host_info.port,
realm_name);
} else {
serf_context_t *ctx = conn->ctx;
return apr_psprintf(pool, "<http://%s:%d> %s",
ctx->proxy_address->hostname,
ctx->proxy_address->port,
realm_name);
}
}
serf__authn_info_t *serf__get_authn_info_for_server(serf_connection_t *conn)
{
serf_context_t *ctx = conn->ctx;
serf__authn_info_t *authn_info;
authn_info = apr_hash_get(ctx->server_authn_info, conn->host_url,
APR_HASH_KEY_STRING);
if (!authn_info) {
authn_info = apr_pcalloc(ctx->pool, sizeof(serf__authn_info_t));
apr_hash_set(ctx->server_authn_info,
apr_pstrdup(ctx->pool, conn->host_url),
APR_HASH_KEY_STRING, authn_info);
}
return authn_info;
}
apr_status_t serf__auth_setup_connection(peer_t peer,
serf_connection_t *conn)
{
serf__authn_info_t *authn_info;
serf_context_t *ctx = conn->ctx;
apr_status_t status = APR_SUCCESS;
if (peer == PROXY) {
authn_info = &ctx->proxy_authn_info;
if (authn_info->scheme) {
status = authn_info->scheme->init_conn_func(authn_info->scheme,
SERF_AUTHN_CODE_PROXY,
conn, conn->pool);
}
}
else {
authn_info = serf__get_authn_info_for_server(conn);
if (authn_info->scheme) {
status = authn_info->scheme->init_conn_func(authn_info->scheme,
SERF_AUTHN_CODE_HOST,
conn, conn->pool);
}
}
return status;
}
apr_status_t serf__auth_setup_request(peer_t peer,
serf_request_t *request,
const char *method,
const char *uri,
serf_bucket_t *hdrs_bkt)
{
if (peer == PROXY && request->conn->ctx->proxy_authn_info.scheme) {
serf__authn_info_t *authn_info = &request->conn->ctx->proxy_authn_info;
authn_info->scheme->setup_request_func(authn_info->scheme,
peer, 0,
request->conn, request,
method, uri,
hdrs_bkt);
}
else if (peer == HOST)
{
serf__authn_info_t *authn_info;
authn_info = serf__get_authn_info_for_server(request->conn);
if (authn_info->scheme) {
authn_info->scheme->setup_request_func(authn_info->scheme,
HOST, 0, request->conn,
request, method, uri,
hdrs_bkt);
}
}
return APR_SUCCESS;
}
/* User-defined authentication providers. */
/* The type range for user-defined schemes: */
#if UINT_MAX >= 0xFFFFFFFFFFFFFFFF /* Integers are at least 64 bits wide. */
#define SERF__AUTHN_USER_FIRST 0x80000000000u /* 43 built-in + 21 user. */
#elif UINT_MAX >= 0xFFFFFFFF /* Integers are at least 32 bits wide. */
#define SERF__AUTHN_USER_FIRST 0x200000u /* 21 built-in + 11 user. */
#else /* Integers are at least 16 bits wide. */
#define SERF__AUTHN_USER_FIRST 0x800u /* 11 built-in + 5 user. */
#endif
/* The magic number in the scheme struct. serfauthnschemes */
const apr_uint64_t serf__authn_user__magic = 0x5e6fa02895c8e3e5;
/* The available user-defined scheme types. This is a bit mask based on the
first scheme, later modified to account for any overflow from the built-in
schemes list (not likely, but safey). Should be const, but it's modified
during one-time initialization.
Access is controlled by authn_schemes_guard. */
static unsigned int user_authn_type_mask = ~(SERF__AUTHN_USER_FIRST - 1u);
/* Access to the above from other modules, made const. */
const unsigned int *const serf__authn_user__type_mask = &user_authn_type_mask;
/* The currently registered user-defined scheme types.
Access is controlled by authn_schemes_guard. */
static unsigned int user_authn_registered = 0;
/* Find the next available bit for a user-defined authentication
scheme. Computes an available bit, using user_authn_type_mask
and user_authn_registered. Returns 0 if there's no more room for
user-defined schemes.
The authn_schemes_guard mutex must be locked. */
static unsigned int find_next_user_scheme_type(void)
{
const unsigned int avail = user_authn_type_mask & ~user_authn_registered;
/* For the source of this horrible hack, see:
https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
return avail & ~(avail & (avail - 1));
Along comes clang and optimizes the above to just two instructions... */
return avail & -avail;
}
/* Pool cleanup handler for user-defined schemes. */
static apr_status_t cleanup_user_scheme(void* data)
{
const serf__authn_scheme_t *const authn_scheme = data;
const serf__authn_scheme_t *slot = NULL;
int index;
apr_status_t lock_status = lock_authn_schemes(NULL);
if (lock_status)
return lock_status;
/* Find the scheme in the table. */
for (index = 0; index < AUTHN_SCHEMES_SIZE; ++index) {
slot = serf_authn_schemes[index];
if (slot == NULL || slot == authn_scheme)
break;
}
if (slot != NULL) {
/* Remove the scheme type from the registered mask. */
user_authn_registered &= ~slot->type;
/* Remove the scheme from the table, moving the other
schemes back over its position. */
for (; slot != NULL && index < AUTHN_SCHEMES_SIZE; ++index)
serf_authn_schemes[index] = slot = serf_authn_schemes[index + 1];
}
lock_status = unlock_authn_schemes(NULL);
if (lock_status)
return lock_status;
return APR_SUCCESS;
}
apr_status_t serf_authn_register_scheme(
int *type,
serf_context_t *ctx,
const char *name, void *baton, int flags,
serf_authn_init_conn_func_t init_conn,
serf_authn_get_realm_func_t get_realm,
serf_authn_handle_func_t handle,
serf_authn_setup_request_func_t setup_request,
serf_authn_validate_response_func_t validate_response,
apr_pool_t *result_pool)
{
serf_config_t *const config = ctx->config;
serf__authn_scheme_t *authn_scheme;
apr_status_t lock_status;
apr_status_t status;
unsigned int scheme_type;
const char *key;
int index;
serf__log(LOGLVL_INFO, LOGCOMP_AUTHN, __FILE__, config,
"Registering user-defined scheme %s", name);
*type = SERF_AUTHN_NONE;
authn_scheme = apr_palloc(result_pool, sizeof(*authn_scheme));
/* Generate a lower-case key for the scheme. */
key = serf__tolower(name, result_pool);
authn_scheme->name = apr_pstrdup(result_pool, name);
authn_scheme->key = key;
/* user_scheme->type = ?; Will be updated later, under lock. */
authn_scheme->init_conn_func = serf__authn_user__init_conn;
authn_scheme->handle_func = serf__authn_user__handle;
authn_scheme->setup_request_func = serf__authn_user__setup_request;
authn_scheme->validate_response_func = serf__authn_user__validate_response;
/* User-defined scheme data. */
authn_scheme->user_magic = serf__authn_user__magic;
authn_scheme->user_pool = result_pool;
authn_scheme->user_flags = flags;
authn_scheme->user_baton = baton;
authn_scheme->user_init_conn_func = init_conn;
authn_scheme->user_get_realm_func = get_realm;
authn_scheme->user_handle_func = handle;
authn_scheme->user_setup_request_func = setup_request;
authn_scheme->user_validate_response_func = validate_response;
lock_status = lock_authn_schemes(config);
if (lock_status) {
serf__log_nopref(LOGLVL_INFO, LOGCOMP_AUTHN, config,
", lock failed %d\n", lock_status);
return lock_status;
}
scheme_type = find_next_user_scheme_type();
if (!scheme_type) {
status = APR_ENOSPC;
goto cleanup;
}
status = APR_SUCCESS;
/* Scan the array for a free slot and also check that this
scheme type hasn't been used yet. */
for (index = 0; index < AUTHN_SCHEMES_SIZE; ++index)
{
const serf__authn_scheme_t *const slot = serf_authn_schemes[index];
if (slot == NULL)
break;
if (slot->type & scheme_type || 0 == strcmp(slot->key, key)) {
/* We somehow managed to register the same thing twice. */
status = APR_EEXIST;
goto cleanup;
}
}
if (index >= AUTHN_SCHEMES_SIZE) {
/* No more space in the table. Shouldn't be possible; if we got this
far, we have a valid scheme type, and as long as we have a scheme
type, there should be space for the scheme in the table. */
status = APR_ENOSPC;
goto cleanup;
}
/* Insert into the slot, and add the sentinel. */
authn_scheme->type = scheme_type;
serf_authn_schemes[index] = authn_scheme;
serf_authn_schemes[index + 1] = NULL;
apr_pool_cleanup_register(authn_scheme->user_pool, authn_scheme,
cleanup_user_scheme, apr_pool_cleanup_null);
*type = scheme_type;
/* Add the scheme type to the registered mask. */
user_authn_registered |= scheme_type;
cleanup:
lock_status = unlock_authn_schemes(config);
if (lock_status) {
serf__log_nopref(LOGLVL_INFO, LOGCOMP_AUTHN, config,
", unlock failed %d, status %d\n",
lock_status, status);
return lock_status;
}
serf__log_nopref(LOGLVL_INFO, LOGCOMP_AUTHN, config,
", status %d\n", status);
return status;
}
apr_status_t serf_authn_unregister_scheme(serf_context_t *ctx,
int type,
const char *name,
apr_pool_t *scratch_pool)
{
serf_config_t *const config = ctx->config;
const serf__authn_scheme_t *authn_scheme = NULL;
const unsigned int scheme_type = type;
apr_status_t lock_status;
apr_status_t status;
const char *key;
int index;
serf__log(LOGLVL_INFO, LOGCOMP_AUTHN, __FILE__, config,
"Unregistering user-defined scheme %s", name);
/* Generate a lower-case key for the scheme. */
key = serf__tolower(name, scratch_pool);
lock_status = lock_authn_schemes(config);
if (lock_status) {
serf__log_nopref(LOGLVL_INFO, LOGCOMP_AUTHN, config,
", lock failed %d\n", lock_status);
return lock_status;
}
status = APR_SUCCESS;
/* Look for the scheme in the table. */
for (index = 0; index < AUTHN_SCHEMES_SIZE; ++index)
{
authn_scheme = serf_authn_schemes[index];
if (authn_scheme == NULL) {
status = APR_ENOENT;
goto cleanup;
}
if (authn_scheme->type == scheme_type
&& 0 == strcmp(authn_scheme->key, key))
break;
}
if (index >= AUTHN_SCHEMES_SIZE) {
/* The scheme wasn't registered */
status = APR_ENOENT;
goto cleanup;
}
/* Move all the following schemes back. This is a memmove, but
it doesn't make much sense to use that since we don't knkow
how many schemes are left after this one. */
for (; index < AUTHN_SCHEMES_SIZE; ++index)
{
serf_authn_schemes[index] = serf_authn_schemes[index + 1];
if (serf_authn_schemes[index] == NULL)
break;
}
apr_pool_cleanup_kill(authn_scheme->user_pool, authn_scheme,
cleanup_user_scheme);
/* Remove the scheme type from the registered mask. */
user_authn_registered &= ~scheme_type;
cleanup:
lock_status = unlock_authn_schemes(config);
if (lock_status) {
serf__log_nopref(LOGLVL_INFO, LOGCOMP_AUTHN, config,
", unlock failed %d, status %d\n",
lock_status, status);
return lock_status;
}
serf__log_nopref(LOGLVL_INFO, LOGCOMP_AUTHN, config,
", status %d\n", status);
return status;
}
#if APR_HAS_THREADS
/* Unfortunately APR does not provide a statically-initialized mutex type, so we
use a simple spinlock to make sure that authn_schemes_guard is initialized
exactly once. This includes creating a detached global pool where the mutex
will be allocated ...
... yuck. */
static apr_status_t do_init_authn_schemes_guard(void *baton)
{
serf_config_t *const config = baton;
unsigned int builtin_types;
apr_allocator_t *allocator;
apr_status_t status;
int index;
serf__log(LOGLVL_DEBUG, LOGCOMP_AUTHN, __FILE__, config,
"Initializing authn schemes mutex");
/* Create a self-contained root pool for the mutex. */
status = apr_allocator_create(&allocator);
if (status || !allocator) {
status = status ? status : APR_ENOMEM;
goto finish;
}
status = apr_pool_create_ex(&authn_schemes_guard_pool,
NULL, NULL, allocator);
if (status || !authn_schemes_guard_pool) {
status = status ? status : APR_ENOMEM;
goto finish;
}
#if APR_POOL_DEBUG
apr_pool_tag(authn_schemes_guard_pool, "serf-authn-guard");
#endif
status = apr_thread_mutex_create(&authn_schemes_guard,
APR_THREAD_MUTEX_DEFAULT,
authn_schemes_guard_pool);
if (status || !authn_schemes_guard) {
status = status ? status : APR_ENOMEM;
goto finish;
}
/* Adjust the mask of available user-defined schemes. */
builtin_types = 0;
for (index = 0; serf_authn_schemes[index]; ++index)
builtin_types |= serf_authn_schemes[index]->type;
user_authn_type_mask &= ~builtin_types;
finish:
serf__log_nopref(LOGLVL_DEBUG, LOGCOMP_AUTHN, config,
", status %d\n", status);
return status;
}
static apr_status_t init_authn_schemes_guard(serf_config_t *config)
{
SERF__DECLARE_STATIC_INIT_ONCE_CONTEXT(init_ctx);
return serf__init_once(&init_ctx, do_init_authn_schemes_guard, config);
}
#endif /* APR_HAS_THREADS */