Author: brane
Date: Sat Jul 5 07:03:43 2025
New Revision: 1926970
URL: http://svn.apache.org/viewvc?rev=1926970&view=rev
Log:
On the user-defined-authn branch: sync with trunk r1926949.
Modified:
serf/branches/user-defined-authn/ (props changed)
serf/branches/user-defined-authn/CMakeLists.txt
serf/branches/user-defined-authn/README
serf/branches/user-defined-authn/SConstruct
serf/branches/user-defined-authn/buckets/deflate_buckets.c
serf/branches/user-defined-authn/buckets/hpack_buckets.c
serf/branches/user-defined-authn/buckets/ssl_buckets.c
serf/branches/user-defined-authn/serf_bucket_types.h
serf/branches/user-defined-authn/test/serf_get.c
serf/branches/user-defined-authn/test/test_ssl.c
Propchange: serf/branches/user-defined-authn/
------------------------------------------------------------------------------
Merged /serf/trunk:r1926939-1926969
Merged /serf/branches/PR-8:r1926893-1926949
Modified: serf/branches/user-defined-authn/CMakeLists.txt
URL:
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/CMakeLists.txt?rev=1926970&r1=1926969&r2=1926970&view=diff
==============================================================================
--- serf/branches/user-defined-authn/CMakeLists.txt (original)
+++ serf/branches/user-defined-authn/CMakeLists.txt Sat Jul 5 07:03:43 2025
@@ -76,6 +76,7 @@ endif()
if(NOT CMAKE_BUILD_TYPE)
if(DEBUG)
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Default to debug build." FORCE)
+ set(SERF_MAINTAINER_MODE TRUE)
else()
set(CMAKE_BUILD_TYPE Release CACHE STRING "Default to release build."
FORCE)
endif()
@@ -307,6 +308,11 @@ CheckNotFunction("X509_STORE_CTX_get0_ch
CheckNotFunction("ASN1_STRING_get0_data" "NULL"
"SERF_NO_SSL_ASN1_STRING_GET0_DATA"
"openssl/asn1.h" "${OPENSSL_INCLUDE_DIR}"
${OPENSSL_LIBRARIES} ${SERF_STANDARD_LIBRARIES})
+CheckFunction("OSSL_STORE_open_ex"
+ "NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL"
+ "SERF_HAVE_OSSL_STORE_OPEN_EX" "openssl/store.h"
+ "${OPENSSL_INCLUDE_DIR}" ${OPENSSL_LIBRARIES}
+ ${SERF_STANDARD_LIBRARIES})
CheckFunction("CRYPTO_set_locking_callback" "NULL"
"SERF_HAVE_SSL_LOCKING_CALLBACKS"
"openssl/crypto.h" "${OPENSSL_INCLUDE_DIR}"
${OPENSSL_LIBRARIES} ${SERF_STANDARD_LIBRARIES})
Modified: serf/branches/user-defined-authn/README
URL:
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/README?rev=1926970&r1=1926969&r2=1926970&view=diff
==============================================================================
--- serf/branches/user-defined-authn/README (original)
+++ serf/branches/user-defined-authn/README Sat Jul 5 07:03:43 2025
@@ -223,16 +223,15 @@ or, with a multi-config generator:
$ ctest -C Release
-This is equivalent to
+This is equivalent to:
$ cmake --build out --target test
-or
+or:
$ cmake --build out --config Release --target test
-(or, on Windows using the Visual Studio generator, which always has to be
- special and different for no discernible benefit:
+or, on Windows using the Visual Studio generator:
$ cmake --build out --config Release --target run_tests
)
Modified: serf/branches/user-defined-authn/SConstruct
URL:
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/SConstruct?rev=1926970&r1=1926969&r2=1926970&view=diff
==============================================================================
--- serf/branches/user-defined-authn/SConstruct (original)
+++ serf/branches/user-defined-authn/SConstruct Sat Jul 5 07:03:43 2025
@@ -59,6 +59,9 @@ except TypeError:
custom_tests['CheckFunc'] = build.scons_extras.CheckFunc
print('warning: replaced Conftest.CheckFunc() for SCons version < 4.7.')
+# By default, we silence all warnings when compiling MockHTTP.
+# Set this to True to show them instead.
+SHOW_MOCKHTTP_WARNINGS = False
HEADER_FILES = ['serf.h',
'serf_bucket_types.h',
@@ -364,6 +367,13 @@ if sys.platform != 'win32':
# env.Append(CCFLAGS=['-g'])
env.SerfAppendIf(['CFLAGS', 'CCFLAGS'], r'-g\S*', CCFLAGS=['-g'])
env.Append(CPPDEFINES=['DEBUG', '_DEBUG'])
+ env.Append(CCFLAGS=['-Wimplicit-function-declaration',
+ '-Wmissing-variable-declarations',
+ '-Wunreachable-code',
+ '-Wshorten-64-to-32',
+ '-Wno-system-headers',
+ '-Wextra-tokens',
+ '-Wnewline-eof'])
else:
# env.Append(CCFLAGS=['-O2'])
env.SerfAppendIf(['CFLAGS', 'CCFLAGS'], r'-O\S*', CCFLAGS=['-O2'])
@@ -418,6 +428,9 @@ if export_filter is not None:
env.GenExports(target=export_filter, source=HEADER_FILES)
env.Depends(lib_shared, export_filter)
+# We do not want or need OpenSSL's compatibility macros.
+env.Append(CPPDEFINES=['OPENSSL_NO_DEPRECATED'])
+
# Define OPENSSL_NO_STDIO to prevent using _fp() API.
env.Append(CPPDEFINES=['OPENSSL_NO_STDIO'])
@@ -582,7 +595,7 @@ else:
env.Append(LIBPATH=['$OPENSSL/lib'])
if brotli:
- brotli_libs = '-lbrotlicommon -lbrotlienc'
+ brotli_libs = '-lbrotlicommon -lbrotlidec'
env.Append(CPPPATH=['$BROTLI/include'],
LIBPATH=['$BROTLI/lib'])
else:
@@ -623,6 +636,9 @@ if conf.CheckFunc('OpenSSL_version_num',
env.Append(CPPDEFINES=['SERF_HAVE_OPENSSL_VERSION_NUM'])
if conf.CheckFunc('SSL_set_alpn_protos', ssl_includes, 'C', 'NULL, NULL, 0'):
env.Append(CPPDEFINES=['SERF_HAVE_OPENSSL_ALPN'])
+if conf.CheckFunc('OSSL_STORE_open_ex', ssl_includes, 'C',
+ 'NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL'):
+ env.Append(CPPDEFINES=['SERF_HAVE_OSSL_STORE_OPEN_EX'])
if conf.CheckType('OSSL_HANDSHAKE_STATE', ssl_includes):
env.Append(CPPDEFINES=['SERF_HAVE_OSSL_HANDSHAKE_STATE'])
env = conf.Finish()
@@ -724,24 +740,45 @@ env.Alias('install', ['install-lib', 'in
### make move to a separate scons file in the test/ subdir?
tenv = env.Clone()
+tenv.Append(CPPDEFINES=['MOCKHTTP_OPENSSL'])
+
+# Build the MockHTTP static library. MockHTTP needs C99 and OpenSSL's
+# deprecated APIs. Also silence all warnings from MockHTTP.
+mockenv = tenv.Clone()
+mockenv.Replace(CFLAGS = [f.replace('-std=c89', '-std=c99')
+ for f in mockenv['CFLAGS']])
+mockenv.Replace(CCFLAGS = list(
+ filter(lambda f: (SHOW_MOCKHTTP_WARNINGS
+ # NOTE: SCons flags are sometimes tuples, not strings.
+ # In those cases, the first element is the flag
+ # and the rest ar the flag's value(s).
+ or (# GCC-like warning flags
+ not re.match(r'^\s*-W[a-z][a-z-]+',
+ f if type(f) == type('') else f[0])
+ # MSVC-like warning flags
+ and not re.match(r'^\s*/(W|w[de])\d+',
+ f if type(f) == type('') else f[0]))),
+ mockenv['CCFLAGS'])
+))
+if not SHOW_MOCKHTTP_WARNINGS:
+ mockenv.Append(CCFLAGS = ['-w' if sys.platform != 'win32' else '/w'])
+mockenv.Replace(CPPDEFINES = list(filter(lambda d: d !=
'OPENSSL_NO_DEPRECATED',
+ mockenv['CPPDEFINES'])))
+
+mockhttpinc = mockenv.StaticLibrary('mockhttpinc',
+ ['test/MockHTTPinC/MockHTTP.c',
+ 'test/MockHTTPinC/MockHTTP_server.c'])
# Check if long-running tests should be enabled
if tenv.get('ENABLE_SLOW_TESTS', None):
tenv.Append(CPPDEFINES=['SERF_TEST_DEFLATE_4GBPLUS_BUCKETS'])
-# MockHTTP requires C99 standard, so use it for the test suite.
-cflags = tenv['CFLAGS']
-tenv.Replace(CFLAGS = [f.replace('-std=c89', '-std=c99') for f in cflags])
-
-tenv.Append(CPPDEFINES=['MOCKHTTP_OPENSSL'])
-
TEST_PROGRAMS = [ 'serf_get', 'serf_response', 'serf_request', 'serf_spider',
'serf_httpd',
'test_all', 'serf_bwtp' ]
-if sys.platform == 'win32':
- TEST_EXES = [ os.path.join('test', '%s.exe' % (prog)) for prog in
TEST_PROGRAMS ]
-else:
- TEST_EXES = [ os.path.join('test', '%s' % (prog)) for prog in TEST_PROGRAMS ]
+
+_exe = '.exe' if sys.platform == 'win32' else ''
+TEST_EXES = [os.path.join('test', '%s%s' % (prog, _exe)) for prog in
TEST_PROGRAMS]
check_script = env.File('build/check.py').rstr()
test_dir = env.File('test/test_all.c').rfile().get_dir()
@@ -769,18 +806,18 @@ testall_files = [
'test/mock_buckets.c',
'test/mock_sock_buckets.c',
'test/test_ssl.c',
- 'test/MockHTTPinC/MockHTTP.c',
- 'test/MockHTTPinC/MockHTTP_server.c',
]
# We link the programs explicitly against the static libraries, to allow
# access to private functions
+mocklib = mockhttpinc[0].rfile().abspath
+serflib = lib_static[0].rfile().abspath
for proggie in TEST_EXES:
if 'test_all' in proggie:
- tenv.Program(proggie, testall_files + [LIBNAME + env['LIBSUFFIX']])
+ tenv.Program(proggie, testall_files + [mocklib, serflib])
else:
- tenv.Program(target = proggie, source = [proggie.replace('.exe','') + '.c',
- LIBNAME + env['LIBSUFFIX']])
+ tenv.Program(target=proggie,
+ source=[proggie.replace('.exe','') + '.c', serflib])
# HANDLE CLEANING
Modified: serf/branches/user-defined-authn/buckets/deflate_buckets.c
URL:
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/buckets/deflate_buckets.c?rev=1926970&r1=1926969&r2=1926970&view=diff
==============================================================================
--- serf/branches/user-defined-authn/buckets/deflate_buckets.c (original)
+++ serf/branches/user-defined-authn/buckets/deflate_buckets.c Sat Jul 5
07:03:43 2025
@@ -204,7 +204,7 @@ static void serf_deflate_destroy_and_dat
if ((ctx->state > STATE_INIT && ctx->state <= STATE_FINISH)
|| (ctx->state > STATE_COMPRESS_INIT
- && ctx->state < STATE_COMPRESS_FINISH))
+ && ctx->state <= STATE_COMPRESS_FINISH))
{
if (ctx->memLevel >= 0)
deflateEnd(&ctx->zstream);
Modified: serf/branches/user-defined-authn/buckets/hpack_buckets.c
URL:
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/buckets/hpack_buckets.c?rev=1926970&r1=1926969&r2=1926970&view=diff
==============================================================================
--- serf/branches/user-defined-authn/buckets/hpack_buckets.c (original)
+++ serf/branches/user-defined-authn/buckets/hpack_buckets.c Sat Jul 5
07:03:43 2025
@@ -1396,9 +1396,8 @@ handle_read_entry_and_clear(serf_hpack_d
serf_hpack_table_t *tbl = ctx->tbl;
const char *keep_key = NULL;
const char *keep_val = NULL;
- apr_status_t status;
- char own_key;
- char own_val;
+ bool own_key;
+ bool own_val;
serf__log(LOGLVL_INFO, SERF_LOGCOMP_PROTOCOL, __FILE__, ctx->config,
"Parsed from HPACK: %.*s: %.*s\n",
@@ -1476,9 +1475,11 @@ handle_read_entry_and_clear(serf_hpack_d
if (ctx->reuse_item)
{
- status = hpack_table_get(ctx->reuse_item, tbl,
- &keep_key, NULL,
- &keep_val, NULL);
+ /* hpack_table_get() does not modify its output arguments if
+ it returns an error, so we ignore the return value here. */
+ hpack_table_get(ctx->reuse_item, tbl,
+ &keep_key, NULL,
+ &keep_val, NULL);
}
own_key = (ctx->key && ctx->key != keep_key);
Modified: serf/branches/user-defined-authn/buckets/ssl_buckets.c
URL:
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/buckets/ssl_buckets.c?rev=1926970&r1=1926969&r2=1926970&view=diff
==============================================================================
--- serf/branches/user-defined-authn/buckets/ssl_buckets.c (original)
+++ serf/branches/user-defined-authn/buckets/ssl_buckets.c Sat Jul 5 07:03:43
2025
@@ -44,6 +44,15 @@
#ifndef OPENSSL_NO_OCSP /* requires openssl 0.9.7 or later */
#include <openssl/ocsp.h>
#endif
+#if defined(SERF_HAVE_OSSL_STORE_OPEN_EX)
+#include <openssl/store.h>
+#include <openssl/evp.h>
+#include <openssl/safestack.h>
+#include <openssl/ui.h>
+#ifndef sk_EVP_PKEY_new_null
+DEFINE_STACK_OF(EVP_PKEY)
+#endif
+#endif
#ifndef APR_ARRAY_PUSH
#define APR_ARRAY_PUSH(ary,type) (*((type *)apr_array_push(ary)))
@@ -117,6 +126,8 @@
*
*/
+static int ssl_x509_ex_data_idx = -1;
+
typedef struct bucket_list {
serf_bucket_t *bucket;
struct bucket_list *next;
@@ -177,12 +188,20 @@ struct serf_ssl_context_t {
apr_pool_t *cert_pw_cache_pool;
const char *cert_pw_success;
+ /* Cert uri callbacks */
+ serf_ssl_need_cert_uri_t cert_uri_callback;
+ void *cert_uri_userdata;
+ apr_pool_t *cert_uri_cache_pool;
+ const char *cert_uri_success;
+
/* Server cert callbacks */
serf_ssl_need_server_cert_t server_cert_callback;
serf_ssl_server_cert_chain_cb_t server_cert_chain_callback;
void *server_cert_userdata;
const char *cert_path;
+ const char *cert_uri;
+ const char *cert_pw;
X509 *cached_cert;
EVP_PKEY *cached_cert_pw;
@@ -1502,6 +1521,12 @@ static apr_status_t do_init_libraries(vo
OpenSSL_add_all_algorithms();
#endif
+#if defined(SERF_HAVE_OSSL_STORE_OPEN_EX)
+ if (ssl_x509_ex_data_idx < 0) {
+ ssl_x509_ex_data_idx = X509_get_ex_new_index(0, NULL, NULL, NULL,
NULL);
+ }
+#endif
+
#if APR_HAS_THREADS && defined(SERF_HAVE_SSL_LOCKING_CALLBACKS)
numlocks = CRYPTO_num_locks();
apr_pool_create(&ssl_pool, NULL);
@@ -1533,10 +1558,49 @@ static apr_status_t init_ssl_libraries(v
return serf__init_once(&init_ctx, do_init_libraries, NULL);
}
+#if defined(SERF_HAVE_OSSL_STORE_OPEN_EX)
+
+static int ssl_pass_cb(UI *ui, UI_STRING *uis)
+{
+ serf_ssl_context_t *ctx = UI_get0_user_data(ui);
+
+ const char *password;
+
+ if (ctx->cert_pw_success) {
+ password = ctx->cert_pw_success;
+ ctx->cert_pw_success = NULL;
+ }
+ else if (ctx->cert_pw_callback) {
+ if (APR_SUCCESS != ctx->cert_pw_callback(ctx->cert_pw_userdata,
+ ctx->cert_uri,
+ &password))
+ return 0;
+ }
+ else {
+ return 0;
+ }
+
+ UI_set_result(ui, uis, password);
+
+ ctx->cert_pw = apr_pstrdup(ctx->pool, password);
+
+ return 1;
+}
+
+#endif
+
static int ssl_need_client_cert(SSL *ssl, X509 **cert, EVP_PKEY **pkey)
{
serf_ssl_context_t *ctx = SSL_get_app_data(ssl);
+#if defined(SERF_HAVE_OSSL_STORE_OPEN_EX)
+ STACK_OF(X509) *leaves;
+ STACK_OF(X509) *intermediates;
+ STACK_OF(EVP_PKEY) *keys;
+ X509_STORE *requests;
+ UI_METHOD *ui_method;
+#endif
apr_status_t status;
+ int retrying_success = 0;
serf__log(LOGLVL_DEBUG, LOGCOMP_SSL, __FILE__, ctx->config,
"Server requests a client certificate.\n");
@@ -1547,6 +1611,212 @@ static int ssl_need_client_cert(SSL *ssl
return 1;
}
+#if defined(SERF_HAVE_OSSL_STORE_OPEN_EX)
+
+ /* until further notice */
+ *cert = NULL;
+ *pkey = NULL;
+
+ leaves = sk_X509_new_null();
+ intermediates = sk_X509_new_null();
+ keys = sk_EVP_PKEY_new_null();
+ requests = X509_STORE_new();
+
+ ui_method = UI_create_method("passphrase");
+ UI_method_set_reader(ui_method, ssl_pass_cb);
+
+ while (ctx->cert_uri_callback) {
+ const char *cert_uri = NULL;
+ OSSL_STORE_CTX *store = NULL;
+ OSSL_STORE_INFO *info;
+ X509 *c;
+ STACK_OF(X509_NAME) *requested;
+ int type;
+
+ retrying_success = 0;
+
+ if (ctx->cert_uri_success) {
+ status = APR_SUCCESS;
+ cert_uri = ctx->cert_uri_success;
+ ctx->cert_uri_success = NULL;
+ retrying_success = 1;
+ } else {
+ status = ctx->cert_uri_callback(ctx->cert_uri_userdata, &cert_uri);
+ }
+
+ if (status || !cert_uri) {
+ break;
+ }
+
+ ctx->cert_uri = cert_uri;
+
+ /* server side request some certs? this list may be empty */
+ requested = SSL_get_client_CA_list(ssl);
+
+ store = OSSL_STORE_open_ex(cert_uri, NULL, NULL, ui_method, ctx, NULL,
+ NULL, NULL);
+ if (!store) {
+ int err = ERR_get_error();
+ serf__log(LOGLVL_ERROR, LOGCOMP_SSL, __FILE__, ctx->config,
+ "OpenSSL store error (%s): %d %d\n", cert_uri,
+ ERR_GET_LIB(err), ERR_GET_REASON(err));
+ break;
+ }
+
+ /* walk the store, what are we working with */
+
+ while (!OSSL_STORE_eof(store)) {
+ info = OSSL_STORE_load(store);
+
+ if (!info) {
+ break;
+ }
+
+ type = OSSL_STORE_INFO_get_type(info);
+ if (type == OSSL_STORE_INFO_CERT) {
+ X509 *c = OSSL_STORE_INFO_get1_CERT(info);
+
+ int n, i;
+
+ int is_ca = X509_check_ca(c);
+
+ /* split into leaves and intermediate certs */
+ if (is_ca) {
+ sk_X509_push(intermediates, c);
+ }
+ else {
+ sk_X509_push(leaves, c);
+ }
+
+ /* any cert with an issuer matching our requested CAs is also
+ * added to the requests list, except for leaf certs which are
+ * marked as requested with a flag so we can skip the chain
+ * check later. */
+ n = sk_X509_NAME_num(requested);
+ for (i = 0; i < n; ++i) {
+ X509_NAME *name = sk_X509_NAME_value(requested, i);
+ if (X509_NAME_cmp(name, X509_get_issuer_name(c)) == 0) {
+ if (is_ca) {
+ X509_STORE_add_cert(requests, c);
+ }
+ else {
+ X509_set_ex_data(c, ssl_x509_ex_data_idx,
+ (void *)1);
+ }
+ }
+ }
+
+ } else if (type == OSSL_STORE_INFO_PKEY) {
+ EVP_PKEY *k = OSSL_STORE_INFO_get1_PKEY(info);
+
+ sk_EVP_PKEY_push(keys, k);
+ }
+
+ OSSL_STORE_INFO_free(info);
+ }
+
+ /* FIXME: openssl error checking goes here */
+
+ OSSL_STORE_close(store);
+
+ /* walk the leaf certificates, choose the best one */
+
+ while ((c = sk_X509_pop(leaves))) {
+
+ EVP_PKEY *k = NULL;
+ int i, n, found = 0;
+
+ /* no key, skip */
+ n = sk_EVP_PKEY_num(keys);
+ for (i = 0; i < n; ++i) {
+ k = sk_EVP_PKEY_value(keys, i);
+ if (X509_check_private_key(c, k)) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ continue;
+ }
+
+ /* CAs requested? if so, skip non matches, if not, accept all */
+ if (sk_X509_NAME_num(requested) &&
+ !X509_get_ex_data(c, ssl_x509_ex_data_idx)) {
+ STACK_OF(X509) *chain;
+
+ chain = X509_build_chain(c, intermediates, requests, 0, NULL,
+ NULL);
+
+ if (!chain) {
+ continue;
+ }
+
+ sk_X509_pop_free(chain, X509_free);
+ }
+
+ /* no best candidate yet? we're in first place */
+ if (!*cert) {
+ EVP_PKEY_up_ref(k);
+ *cert = c; /* don't dup, we're returning this */
+ *pkey = k;
+ continue;
+ }
+
+ /* were we issued after the previous best? */
+ if (ASN1_TIME_compare(X509_get0_notBefore(*cert),
+ X509_get0_notBefore(c)) < 0) {
+ X509_free(*cert);
+ EVP_PKEY_free(*pkey);
+ EVP_PKEY_up_ref(k);
+ *cert = c; /* don't dup, we're returning this */
+ *pkey = k;
+ continue;
+ }
+
+ X509_free(c);
+ }
+
+ break;
+ }
+
+ sk_X509_pop_free(leaves, X509_free);
+ sk_X509_pop_free(intermediates, X509_free);
+ sk_EVP_PKEY_pop_free(keys, EVP_PKEY_free);
+ X509_STORE_free(requests);
+ UI_destroy_method(ui_method);
+
+ /* we settled on a cert and key, cache it for later */
+
+ if (*cert && *pkey) {
+
+ ctx->cached_cert = *cert;
+ ctx->cached_cert_pw = *pkey;
+ if (!retrying_success && ctx->cert_cache_pool) {
+ const char *c;
+
+ c = apr_pstrdup(ctx->cert_cache_pool, ctx->cert_uri);
+
+ apr_pool_userdata_setn(c, "serf:ssl:cert",
+ apr_pool_cleanup_null,
+ ctx->cert_cache_pool);
+ }
+
+ if (!retrying_success && ctx->cert_pw_cache_pool && ctx->cert_pw) {
+ const char *pw;
+
+ pw = apr_pstrdup(ctx->cert_pw_cache_pool,
+ ctx->cert_pw);
+
+ apr_pool_userdata_setn(pw, "serf:ssl:certpw",
+ apr_pool_cleanup_null,
+ ctx->cert_pw_cache_pool);
+ }
+
+ return 1;
+ }
+
+#endif
+
while (ctx->cert_callback) {
const char *cert_path;
apr_file_t *cert_file;
@@ -1554,7 +1824,7 @@ static int ssl_need_client_cert(SSL *ssl
BIO_METHOD *biom;
PKCS12 *p12;
int i;
- int retrying_success = 0;
+ retrying_success = 0;
if (ctx->cert_file_success) {
status = APR_SUCCESS;
@@ -1704,6 +1974,22 @@ void serf_ssl_client_cert_password_set(
}
+void serf_ssl_cert_uri_set(
+ serf_ssl_context_t *context,
+ serf_ssl_need_cert_uri_t callback,
+ void *data,
+ void *cache_pool)
+{
+ context->cert_uri_callback = callback;
+ context->cert_uri_userdata = data;
+ context->cert_cache_pool = cache_pool;
+ if (context->cert_cache_pool) {
+ apr_pool_userdata_get((void**)&context->cert_uri_success,
+ "serf:ssl:certuri", cache_pool);
+ }
+}
+
+
void serf_ssl_server_cert_callback_set(
serf_ssl_context_t *context,
serf_ssl_need_server_cert_t callback,
@@ -1780,6 +2066,7 @@ static serf_ssl_context_t *ssl_init_cont
ssl_ctx->cert_callback = NULL;
ssl_ctx->cert_pw_callback = NULL;
+ ssl_ctx->cert_uri_callback = NULL;
ssl_ctx->server_cert_callback = NULL;
ssl_ctx->server_cert_chain_callback = NULL;
Modified: serf/branches/user-defined-authn/serf_bucket_types.h
URL:
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/serf_bucket_types.h?rev=1926970&r1=1926969&r2=1926970&view=diff
==============================================================================
--- serf/branches/user-defined-authn/serf_bucket_types.h (original)
+++ serf/branches/user-defined-authn/serf_bucket_types.h Sat Jul 5 07:03:43
2025
@@ -600,6 +600,10 @@ typedef apr_status_t (*serf_ssl_need_cer
const char *cert_path,
const char **password);
+typedef apr_status_t (*serf_ssl_need_cert_uri_t)(
+ void *data,
+ const char **cert_uri);
+
/**
* Callback type for server certificate status info and OCSP responses.
* Note that CERT can be NULL in case its called from the OCSP callback.
@@ -616,18 +620,53 @@ typedef apr_status_t (*serf_ssl_server_c
const serf_ssl_certificate_t * const * certs,
apr_size_t certs_len);
+/**
+ * Set a callback to provide a filesystem path to a PKCS12 file.
+ *
+ * This has been replaced by serf_ssl_cert_uri_set(). On Unix
+ * platforms the same path from serf_ssl_client_cert_provider_set()
+ * can be passed to serf_ssl_cert_uri_set(). On Windows the drive
+ * letter will be interpreted by serf_ssl_cert_uri_set() as a scheme,
+ * so the same path will not work, and will need to be escaped as
+ * a file URL instead.
+ */
void serf_ssl_client_cert_provider_set(
serf_ssl_context_t *context,
serf_ssl_need_client_cert_t callback,
void *data,
void *cache_pool);
+/**
+ * Set a callback to provide the password corresponding to the URL of
+ * the client certificate store.
+ *
+ * If the serf_ssl_client_cert_provider_set callback is set, this
+ * password will also be used to decode the PKCS12 file.
+ */
void serf_ssl_client_cert_password_set(
serf_ssl_context_t *context,
serf_ssl_need_cert_password_t callback,
void *data,
void *cache_pool);
+/**
+ * Set a callback to provide the URL of the client certificate store.
+ *
+ * In the absence of a scheme the default scheme is file:, and the file
+ * can point to PKCS12, PEM or other supported certificates and keys.
+ *
+ * With the correct OpenSSL provider configured, URLs can be provided
+ * for pkcs11, tpm2, and other certificate stores.
+ *
+ * On Windows, file paths must be escaped as file: URLs to prevent the
+ * drive letter being intepreted as a scheme.
+ */
+void serf_ssl_cert_uri_set(
+ serf_ssl_context_t *context,
+ serf_ssl_need_cert_uri_t callback,
+ void *data,
+ void *cache_pool);
+
/**
* Set a callback to override the default SSL server certificate validation
* algorithm.
Modified: serf/branches/user-defined-authn/test/serf_get.c
URL:
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/test/serf_get.c?rev=1926970&r1=1926969&r2=1926970&view=diff
==============================================================================
--- serf/branches/user-defined-authn/test/serf_get.c (original)
+++ serf/branches/user-defined-authn/test/serf_get.c Sat Jul 5 07:03:43 2025
@@ -41,6 +41,7 @@ typedef struct app_baton_t {
int use_h2direct;
const char *pem_path;
const char *pem_pwd;
+ const char *cert_uri;
serf_bucket_alloc_t *bkt_alloc;
serf_context_t *serf_ctx;
} app_baton_t;
@@ -80,7 +81,8 @@ static apr_status_t client_cert_pw_cb(vo
{
app_baton_t *ctx = data;
- if (strcmp(cert_path, ctx->pem_path) == 0)
+ if ((ctx->cert_uri && !strcmp(cert_path, ctx->cert_uri)) ||
+ (ctx->pem_path && !strcmp(cert_path, ctx->pem_path)))
{
*password = ctx->pem_pwd;
return APR_SUCCESS;
@@ -89,6 +91,15 @@ static apr_status_t client_cert_pw_cb(vo
return APR_EGENERAL;
}
+static apr_status_t client_cert_uri_cb(void *data, const char **cert_uri)
+{
+ app_baton_t *ctx = data;
+
+ *cert_uri = ctx->cert_uri;
+
+ return APR_SUCCESS;
+}
+
static void print_ssl_cert_errors(int failures)
{
if (failures) {
@@ -235,6 +246,13 @@ static apr_status_t conn_setup(apr_socke
pool);
}
+ if (ctx->cert_uri) {
+ serf_ssl_cert_uri_set(conn_ctx->ssl_ctx,
+ client_cert_uri_cb,
+ ctx,
+ pool);
+ }
+
if (ctx->pem_pwd) {
serf_ssl_client_cert_password_set(conn_ctx->ssl_ctx,
client_cert_pw_cb,
@@ -494,6 +512,7 @@ credentials_callback(char **username,
#define CERTPWD 257
#define HTTP2FLAG 258
#define H2DIRECT 259
+#define CERTURI 260
static const apr_getopt_option_t options[] =
{
@@ -510,6 +529,7 @@ static const apr_getopt_option_t options
{NULL, 'f', 1, "<file> Use the <file> as the request body"},
{NULL, 'p', 1, "<hostname:port> Use the <host:port> as proxy server"},
{"cert", CERTFILE, 1, "<file> Use SSL client certificate <file>"},
+ {"certuri", CERTURI, 1, "<uri> Use SSL client certificate <uri>"},
{"certpwd", CERTPWD, 1, "<password> Password for the SSL client
certificate"},
{NULL, 'r', 1, "<header:value> Use <header:value> as request header"},
{"debug", 'd', 0, "Enable debugging"},
@@ -564,7 +584,7 @@ int main(int argc, const char **argv)
int print_headers, debug, negotiate_http2, use_h2direct;
const char *username = NULL;
const char *password = "";
- const char *pem_path = NULL, *pem_pwd = NULL;
+ const char *pem_path = NULL, *pem_pwd = NULL, *cert_uri = NULL;
apr_getopt_t *opt;
int opt_c;
const char *opt_arg;
@@ -677,6 +697,9 @@ int main(int argc, const char **argv)
case CERTFILE:
pem_path = opt_arg;
break;
+ case CERTURI:
+ cert_uri = opt_arg;
+ break;
case CERTPWD:
pem_pwd = opt_arg;
break;
@@ -731,6 +754,7 @@ int main(int argc, const char **argv)
app_ctx.hostname = url.hostname;
app_ctx.pem_path = pem_path;
app_ctx.pem_pwd = pem_pwd;
+ app_ctx.cert_uri = cert_uri;
context = serf_context_create(pool);
app_ctx.serf_ctx = context;
Modified: serf/branches/user-defined-authn/test/test_ssl.c
URL:
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/test/test_ssl.c?rev=1926970&r1=1926969&r2=1926970&view=diff
==============================================================================
--- serf/branches/user-defined-authn/test/test_ssl.c (original)
+++ serf/branches/user-defined-authn/test/test_ssl.c Sat Jul 5 07:03:43 2025
@@ -1173,6 +1173,76 @@ static void test_ssl_client_certificate(
EndVerify
}
+static apr_status_t
+client_cert_uri_conn_setup(apr_socket_t *skt,
+ serf_bucket_t **input_bkt,
+ serf_bucket_t **output_bkt,
+ void *setup_baton,
+ apr_pool_t *pool)
+{
+ test_baton_t *tb = setup_baton;
+ apr_status_t status;
+
+ status = https_set_root_ca_conn_setup(skt, input_bkt, output_bkt,
+ setup_baton, pool);
+ if (status)
+ return status;
+
+ serf_ssl_cert_uri_set(tb->ssl_context,
+ client_cert_cb,
+ tb,
+ pool);
+
+ serf_ssl_client_cert_password_set(tb->ssl_context,
+ client_cert_pw_cb,
+ tb,
+ pool);
+
+ return APR_SUCCESS;
+}
+
+static void test_ssl_client_certificate_uri(CuTest *tc)
+{
+#if defined(SERF_HAVE_OSSL_STORE_OPEN_EX)
+ test_baton_t *tb = tc->testBaton;
+ handler_baton_t handler_ctx[1];
+ const int num_requests = sizeof(handler_ctx)/sizeof(handler_ctx[0]);
+ apr_status_t status;
+
+
+ /* Set up a test context and a https server */
+ /* The SSL server uses the complete certificate chain to validate the
client
+ certificate. */
+ setup_test_mock_https_server(tb, server_key,
+ all_server_certs,
+ test_clientcert_optional);
+ status = setup_test_client_https_context(tb,
+ client_cert_uri_conn_setup,
+ NULL, /* No server cert callback
*/
+ tb->pool);
+ CuAssertIntEquals(tc, APR_SUCCESS, status);
+
+ Given(tb->mh)
+ ConnectionSetup(ClientCertificateIsValid,
+ ClientCertificateCNEqualTo("Serf Client"))
+
+ GETRequest(URLEqualTo("/"), ChunkedBodyEqualTo("1"),
+ HeaderEqualTo("Host", tb->serv_host))
+ Respond(WithCode(200), WithChunkedBody(""))
+ EndGiven
+
+ create_new_request(tb, &handler_ctx[0], "GET", "/", 1);
+
+ status = run_client_and_mock_servers_loops(tb, num_requests, handler_ctx,
+ tb->pool);
+ CuAssertTrue(tc, tb->result_flags & TEST_RESULT_CLIENT_CERTCB_CALLED);
+ CuAssertTrue(tc, tb->result_flags & TEST_RESULT_CLIENT_CERTPWCB_CALLED);
+ Verify(tb->mh)
+ CuAssert(tc, ErrorMessage, VerifyConnectionSetupOk);
+ EndVerify
+#endif
+}
+
/* Validate that the expired certificate is reported as failure in the
callback. */
static void test_ssl_expired_server_cert(CuTest *tc)
@@ -2752,6 +2822,7 @@ CuSuite *test_ssl(void)
SUITE_ADD_TEST(suite, test_ssl_large_response);
SUITE_ADD_TEST(suite, test_ssl_large_request);
SUITE_ADD_TEST(suite, test_ssl_client_certificate);
+ SUITE_ADD_TEST(suite, test_ssl_client_certificate_uri);
SUITE_ADD_TEST(suite, test_ssl_expired_server_cert);
SUITE_ADD_TEST(suite, test_ssl_future_server_cert);
SUITE_ADD_TEST(suite, test_ssl_revoked_server_cert);