Author: brane
Date: Sat May 31 21:22:00 2025
New Revision: 1926022
URL: http://svn.apache.org/viewvc?rev=1926022&view=rev
Log:
On the user-defined-authn branch: Prepare for registering new
authentication schemes at runtime.
* CMakeLists.txt (SOURCES): Add auth/auth_user_defined.c.
* serf.h
(serf_incoming_response_create, serf_config_authn_types): Tweak docstirng.
(SERF_AUTHN_NONE et al.): Move to new section, "serf authorization".
(SERF_AUTHN_ALL): Widen the value to a whole int's worth.
(serf_authn_register_scheme): Declare prototype (work in progress).
* auth/auth.h
(SERF__AUTHN_USER_FIRST, SERF__AUTHN_USER_LAST): Define limits for the
types of user-defined authentication schemes.
(serf__authn_scheme_t::type): Change to an unsigned int.
(serf__user_authn_scheme_t): New type, descriptor for user-defined
authentication shcemes.
(serf__authn_user__init_conn,
serf__authn_user__handler,
serf__authn_user__setup_request,
serf__authn_user__validate_response): Authn handlers for same.
(serf__authn_user__magic): A magic identifier for serf__user_authn_scheme_t.
* auth/auth.c
(serf_authn_schemes): Make the table large enough to handle as many
authentication schemes as we can support, half of them reserved
for user-defined shcemes.
(authn_schemes_guard,
authn_schemes_guard_pool,
init_authn_schemes_guard,
lock_autn_schemes,
unlock_autn_schemes): Mutex and helpers for serializing access to
serf_authn_schemes if APR threads are available.
(handle_auth_headers): Lock/unlock serf_authn_schemes.
(serf__authn_user__magic): Define here.
(user_authn_scheme_type): Tracks the available user-defined scheme types.
Access is protected by authn_schemes_guard.
(serf_authn_register_scheme): Implement here (work in progress).
* auth/auth_user_defined.c: New file. Private implementation wrapper for
user-defined authentication schemes.
Added:
serf/branches/user-defined-authn/auth/auth_user_defined.c (with props)
Modified:
serf/branches/user-defined-authn/CMakeLists.txt
serf/branches/user-defined-authn/auth/auth.c
serf/branches/user-defined-authn/auth/auth.h
serf/branches/user-defined-authn/serf.h
Modified: serf/branches/user-defined-authn/CMakeLists.txt
URL:
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/CMakeLists.txt?rev=1926022&r1=1926021&r2=1926022&view=diff
==============================================================================
--- serf/branches/user-defined-authn/CMakeLists.txt (original)
+++ serf/branches/user-defined-authn/CMakeLists.txt Sat May 31 21:22:00 2025
@@ -151,6 +151,7 @@ list(APPEND SOURCES
"auth/auth_spnego.c"
"auth/auth_spnego_gss.c"
"auth/auth_spnego_sspi.c"
+ "auth/auth_user_defined.c"
"buckets/aggregate_buckets.c"
"buckets/allocator.c"
"buckets/barrier_buckets.c"
Modified: serf/branches/user-defined-authn/auth/auth.c
URL:
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/auth/auth.c?rev=1926022&r1=1926021&r2=1926022&view=diff
==============================================================================
--- serf/branches/user-defined-authn/auth/auth.c (original)
+++ serf/branches/user-defined-authn/auth/auth.c Sat May 31 21:22:00 2025
@@ -24,8 +24,17 @@
#include <apr.h>
#include <apr_base64.h>
+#include <apr_errno.h>
#include <apr_strings.h>
#include <apr_lib.h>
+#if APR_HAS_THREADS
+# include <stdlib.h>
+# include <apr_atomic.h>
+# include <apr_time.h>
+# include <apr_thread_mutex.h>
+#endif
+
+#include <limits.h>
/* These authentication schemes are in order of decreasing security, the
topmost
scheme will be used first when the server supports it.
@@ -34,8 +43,12 @@
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.
*/
-static const serf__authn_scheme_t *serf_authn_schemes[] = {
+#define AUTHN_SCHEMES_SIZE (sizeof(unsigned int) * CHAR_BIT + 1)
+static const serf__authn_scheme_t *serf_authn_schemes[AUTHN_SCHEMES_SIZE] = {
#ifdef SERF_HAVE_SPNEGO
&serf__spnego_authn_scheme,
#ifdef WIN32
@@ -48,8 +61,55 @@ static const serf__authn_scheme_t *serf_
/* sentinel */
NULL
+
+ /* The rest of the array will be automagically zero-initialized. */
};
+#if APR_HAS_THREADS
+/* Guard access to serf_authn_schemes and user_authn_scheme_type. */
+static apr_thread_mutex_t *authn_schemes_guard;
+static apr_pool_t *authn_schemes_guard_pool;
+static apr_status_t init_authn_schemes_guard();
+#endif
+
+static apr_status_t lock_autn_schemes(serf_config_t *config)
+{
+#if APR_HAS_THREADS
+ apr_status_t status = init_authn_schemes_guard();
+ 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_autn_schemes(serf_config_t *config)
+{
+#if APR_HAS_THREADS
+ apr_status_t status = init_authn_schemes_guard();
+ 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)
@@ -85,7 +145,11 @@ static int handle_auth_headers(int code,
int scheme_idx;
serf_connection_t *conn = request->conn;
serf_context_t *ctx = conn->ctx;
- apr_status_t status;
+ apr_status_t status, lock_status;
+
+ lock_status = lock_autn_schemes(conn->config);
+ if (lock_status)
+ return lock_status;
status = SERF_ERROR_AUTHN_NOT_SUPPORTED;
@@ -166,6 +230,10 @@ static int handle_auth_headers(int code,
authn_info->failed_authn_types |= scheme->type;
}
+ lock_status = unlock_autn_schemes(conn->config);
+ if (lock_status)
+ return lock_status;
+
return status;
}
@@ -504,3 +572,164 @@ apr_status_t serf__auth_setup_request(pe
return APR_SUCCESS;
}
+
+/* User-defined authentication providers. */
+
+/* The magic number in the scheme struct. serfauthnschemes */
+const apr_uint64_t serf__authn_user__magic = 0x5e6fa02895c8e3e5;
+
+/* The next scheme type for user-defined schemes. */
+static unsigned int user_authn_scheme_type = SERF__AUTHN_USER_FIRST;
+
+apr_status_t serf_authn_register_scheme(const char *name,
+ void *baton,
+ apr_pool_t *pool)
+{
+ serf__user_authn_scheme_t *user_scheme;
+ apr_status_t lock_status;
+ apr_status_t status;
+ char *key, *cp;
+ int index;
+
+ if (0 == user_authn_scheme_type)
+ return APR_ENOSPC;
+
+ user_scheme = apr_palloc(pool, sizeof(*user_scheme));
+ user_scheme->magic = serf__authn_user__magic;
+ user_scheme->baton = baton;
+
+ /* Generate a lower-case key for the scheme. */
+ cp = key = apr_pstrdup(pool, name);
+ while (*cp) {
+ *cp = apr_tolower(*cp);
+ ++cp;
+ }
+ user_scheme->authn_scheme.name = apr_pstrdup(pool, name);
+ user_scheme->authn_scheme.key = key;
+ user_scheme->authn_scheme.type = user_authn_scheme_type;
+ user_scheme->authn_scheme.init_conn_func = serf__authn_user__init_conn;
+ user_scheme->authn_scheme.handle_func = serf__authn_user__handler;
+ user_scheme->authn_scheme.setup_request_func =
serf__authn_user__setup_request;
+ user_scheme->authn_scheme.validate_response_func =
serf__authn_user__validate_response;
+
+ lock_status = lock_autn_schemes(NULL /* TODO: whence cometh config? */);
+ if (lock_status)
+ return lock_status;
+
+ 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 - 1; ++index)
+ {
+ const serf__authn_scheme_t **const slot = &serf_authn_schemes[index];
+ if (*slot == NULL)
+ break;
+
+ if ((*slot)->type & user_authn_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 - 1) {
+ /* No more space in the table. Not very likely. */
+ status = APR_ENOSPC;
+ goto cleanup;
+ }
+
+ /* Insert into the slot, and add the sentinel. */
+ serf_authn_schemes[index] = &user_scheme->authn_scheme;
+ serf_authn_schemes[index + 1] = NULL;
+
+ /* Prepare the next scheme type. Will become zero on overflow. */
+ user_authn_scheme_type <<= 1;
+
+ cleanup:
+ lock_status = unlock_autn_schemes(NULL /* TODO: whence cometh config? */);
+ if (lock_status)
+ return lock_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
+ exaclty once. This includes creating a detached global pool where the mutex
+ will be allocated ...
+
+ ... yuck. */
+static apr_status_t init_authn_schemes_guard()
+{
+ static volatile apr_uint32_t global_state = 0; /* uninitialized */
+ static const apr_uint32_t uninitialized = 0;
+ static const apr_uint32_t init_starting = 1;
+ static const apr_uint32_t init_failed = 2;
+ static const apr_uint32_t initialized = 3;
+
+ static apr_status_t init_failed_status = APR_EGENERAL;
+
+ apr_allocator_t *allocator;
+ apr_status_t status;
+ apr_uint32_t current_state = apr_atomic_cas32(&global_state,
+ init_starting,
+ uninitialized);
+ for (;;)
+ {
+ if (current_state == initialized)
+ return APR_SUCCESS;
+
+ if (current_state == uninitialized)
+ /* We're the single initializer, run the init code. */
+ break;
+
+ if (current_state == init_starting)
+ {
+ /* Spin while the initializer is working. */
+ apr_sleep(APR_USEC_PER_SEC / 100);
+ current_state = apr_atomic_cas32(&global_state,
+ uninitialized,
+ uninitialized);
+ continue;
+ }
+
+ if (current_state == init_failed)
+ return init_failed_status;
+
+ /* Not reached, can't happen. */
+ return APR_EGENERAL; /* FIXME: Just abort()? */
+ }
+
+ /* Create a self-contained root pool for the mutex. */
+ status = apr_allocator_create(&allocator);
+ if (status || !allocator)
+ goto error_return;
+
+ status = apr_pool_create_ex(&authn_schemes_guard_pool,
+ NULL, NULL, allocator);
+ if (status || !authn_schemes_guard_pool)
+ goto error_return;
+#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)
+ goto error_return;
+
+ apr_atomic_cas32(&global_state, initialized, init_starting);
+ return APR_SUCCESS;
+
+ error_return:
+ /* We only reach here if something went wrong during initialization. */
+ if (status == APR_SUCCESS) /* Not likely, but don't return "OK". */
+ status = APR_ENOMEM; /* Probable failures are allocations. */
+ init_failed_status = status;
+ apr_atomic_cas32(&global_state, init_failed, init_starting);
+ return status;
+}
+#endif /* APR_HAS_THREADS */
Modified: serf/branches/user-defined-authn/auth/auth.h
URL:
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/auth/auth.h?rev=1926022&r1=1926021&r2=1926022&view=diff
==============================================================================
--- serf/branches/user-defined-authn/auth/auth.h (original)
+++ serf/branches/user-defined-authn/auth/auth.h Sat May 31 21:22:00 2025
@@ -27,6 +27,10 @@
extern "C" {
#endif
+/* User-defined authentication types */
+#define SERF__AUTHN_USER_FIRST 0x10000u /* Won't work with 16-bit ints... */
+#define SERF__AUTHN_USER_LAST ~(~0u >> 1u)
+
/**
* For each authentication scheme we need a handler function of type
* serf__auth_handler_func_t. This function will be called when an
@@ -95,7 +99,7 @@ struct serf__authn_scheme_t {
const char *key;
/* Internal code used for this authn type. */
- int type;
+ unsigned int type;
/* The connection initialization function if any; otherwise, NULL */
serf__init_conn_func_t init_conn_func;
@@ -139,6 +143,61 @@ extern const serf__authn_scheme_t serf__
#endif /* SERF_HAVE_SPNEGO */
+/** User-defined authentication scheme handlers */
+
+/* This struct extends serf__authn_scheme_t with info needed for
+ the user-defined scheme implementation. It's essentially a subclass;
+ per C semantics, the address of the struct is also the address of
+ its first member, so we can safely put a pointer to this struct
+ into serf_authn_schemes. */
+typedef struct serf__user_authn_scheme_t serf__user_authn_scheme_t;
+struct serf__user_authn_scheme_t {
+ serf__authn_scheme_t authn_scheme;
+
+ /* The magic number that helps identify this struct. */
+ apr_uint64_t magic;
+
+ /* The baton used by the callbacks. */
+ void *baton;
+};
+
+
+apr_status_t
+serf__authn_user__init_conn(const serf__authn_scheme_t *scheme,
+ int code,
+ serf_connection_t *conn,
+ apr_pool_t *pool);
+
+apr_status_t
+serf__authn_user__handler(const serf__authn_scheme_t *scheme,
+ int code,
+ serf_request_t *request,
+ serf_bucket_t *response,
+ const char *auth_hdr,
+ const char *auth_attr,
+ apr_pool_t *pool);
+
+apr_status_t
+serf__authn_user__setup_request(const serf__authn_scheme_t *scheme,
+ peer_t peer,
+ int code,
+ serf_connection_t *conn,
+ serf_request_t *request,
+ const char *method,
+ const char *uri,
+ serf_bucket_t *hdrs_bkt);
+
+apr_status_t
+serf__authn_user__validate_response(const serf__authn_scheme_t *scheme,
+ peer_t peer,
+ int code,
+ serf_connection_t *conn,
+ serf_request_t *request,
+ serf_bucket_t *response,
+ apr_pool_t *pool);
+
+extern const apr_uint64_t serf__authn_user__magic;
+
#ifdef __cplusplus
}
#endif
Added: serf/branches/user-defined-authn/auth/auth_user_defined.c
URL:
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/auth/auth_user_defined.c?rev=1926022&view=auto
==============================================================================
--- serf/branches/user-defined-authn/auth/auth_user_defined.c (added)
+++ serf/branches/user-defined-authn/auth/auth_user_defined.c Sat May 31
21:22:00 2025
@@ -0,0 +1,94 @@
+/* ====================================================================
+ * 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 <serf.h>
+#include <serf_private.h>
+
+#include "apr_errno.h"
+#include "auth.h"
+
+
+static const serf__user_authn_scheme_t *
+safe_cast_scheme(const serf__authn_scheme_t *scheme)
+{
+ const serf__user_authn_scheme_t *const user_scheme = (const void *)scheme;
+ if (scheme->type >= SERF__AUTHN_USER_FIRST
+ && user_scheme->magic == serf__authn_user__magic)
+ return user_scheme;
+ return NULL;
+}
+
+apr_status_t
+serf__authn_user__init_conn(const serf__authn_scheme_t *scheme,
+ int code,
+ serf_connection_t *conn,
+ apr_pool_t *pool)
+{
+ if (!safe_cast_scheme(scheme))
+ return APR_EINVAL;
+
+ return APR_ENOTIMPL;
+}
+
+apr_status_t
+serf__authn_user__handler(const serf__authn_scheme_t *scheme,
+ int code,
+ serf_request_t *request,
+ serf_bucket_t *response,
+ const char *auth_hdr,
+ const char *auth_attr,
+ apr_pool_t *pool)
+{
+ if (!safe_cast_scheme(scheme))
+ return APR_EINVAL;
+
+ return APR_ENOTIMPL;
+}
+
+apr_status_t
+serf__authn_user__setup_request(const serf__authn_scheme_t *scheme,
+ peer_t peer,
+ int code,
+ serf_connection_t *conn,
+ serf_request_t *request,
+ const char *method,
+ const char *uri,
+ serf_bucket_t *hdrs_bkt)
+{
+ if (!safe_cast_scheme(scheme))
+ return APR_EINVAL;
+
+ return APR_ENOTIMPL;
+}
+
+apr_status_t
+serf__authn_user__validate_response(const serf__authn_scheme_t *scheme,
+ peer_t peer,
+ int code,
+ serf_connection_t *conn,
+ serf_request_t *request,
+ serf_bucket_t *response,
+ apr_pool_t *pool)
+{
+ if (!safe_cast_scheme(scheme))
+ return APR_EINVAL;
+
+ return APR_ENOTIMPL;
+}
Propchange: serf/branches/user-defined-authn/auth/auth_user_defined.c
------------------------------------------------------------------------------
svn:eol-style = native
Modified: serf/branches/user-defined-authn/serf.h
URL:
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/serf.h?rev=1926022&r1=1926021&r2=1926022&view=diff
==============================================================================
--- serf/branches/user-defined-authn/serf.h (original)
+++ serf/branches/user-defined-authn/serf.h Sat May 31 21:22:00 2025
@@ -631,7 +631,8 @@ apr_status_t serf_incoming_create2(
void *req_setup_baton,
apr_pool_t *client_pool);
-/* Allows creating a response before the request is completely
+/**
+ * Allows creating a response before the request is completely
* read. Will call the response create function if it hasn't
* been called yet.
*
@@ -893,16 +894,10 @@ void serf_config_proxy(
serf_context_t *ctx,
apr_sockaddr_t *address);
-/* Supported authentication types. */
-#define SERF_AUTHN_NONE 0x00
-#define SERF_AUTHN_BASIC 0x01
-#define SERF_AUTHN_DIGEST 0x02
-#define SERF_AUTHN_NTLM 0x04
-#define SERF_AUTHN_NEGOTIATE 0x08
-#define SERF_AUTHN_ALL 0xFF
-
/**
* Define the authentication handlers that serf will try on incoming requests.
+ *
+ * @see @c SERF_AUTHN_ALL etc.
*/
void serf_config_authn_types(
serf_context_t *ctx,
@@ -948,6 +943,39 @@ serf_bucket_t *serf_request_bucket_reque
/** @} */
+/**
+ * @defgroup serf authentication
+ * @ingroup serf
+ * @{
+ */
+
+/* Supported authentication types. */
+#define SERF_AUTHN_NONE 0x00 /**< Authentication type: None */
+#define SERF_AUTHN_BASIC 0x01 /**< Authentication type: Basic */
+#define SERF_AUTHN_DIGEST 0x02 /**< Authentication type: Digest */
+#define SERF_AUTHN_NTLM 0x04 /**< Authentication type: NTLM */
+#define SERF_AUTHN_NEGOTIATE 0x08 /**< Authentication type: Negotiate */
+#define SERF_AUTHN_ALL ~0x00 /**< All authentication types */
+
+/**
+ * Register an autehtication scheme.
+ *
+ * The @a name is the name of the authentication scheme as it appears in the
+ * authorization headers. It must be a valid token as defined in RFC-9110
+ * (see reference, below).
+ *
+ * Internal structures related to this provider will be allocated from @a pool,
+ * take care that its lifetime is long enough.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9110#section-11.1
+ * @since New in 1.4
+ */
+
+apr_status_t serf_authn_register_scheme(const char *name,
+ void *baton,
+ apr_pool_t *pool);
+
+/** @} */
/**
* @defgroup serf buckets