diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e0f9ace5dfd4..62443e48e6c77 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -648,6 +648,10 @@ if (HAVE_UNISTD_H) autocheck_symbol_exists("getpid" "unistd.h" HAVE_GETPID) endif() +if (SVN_ENABLE_RA_SERF) + autocheck_symbol_exists("serf_ssl_cert_uri_set" "serf_bucket_types.h" HAVE_SERF_SSL_CERT_URI_SET) +endif() + include_directories("${CMAKE_CURRENT_BINARY_DIR}") file(GLOB public_headers "subversion/include/*.h") diff --git a/build/ac-macros/serf.m4 b/build/ac-macros/serf.m4 index b3650f61861e3..d0406ef7aaf44 100644 --- a/build/ac-macros/serf.m4 +++ b/build/ac-macros/serf.m4 @@ -89,6 +89,13 @@ AC_DEFUN(SVN_LIB_SERF, svn_lib_serf=$serf_found + if test "$svn_lib_serf" = "yes"; then + save_ldflags="$LDFLAGS" + LDFLAGS="$LDFLAGS $SVN_SERF_LIBS" + AC_CHECK_FUNCS(serf_ssl_cert_uri_set) + LDFLAGS="$save_ldflags" + fi + SVN_DOT_CLANGD([$SVN_SERF_INCLUDES]) AC_SUBST(SVN_SERF_INCLUDES) AC_SUBST(SVN_SERF_LIBS) @@ -140,7 +147,7 @@ AC_DEFUN(SVN_SERF_PKG_CONFIG, [ AC_MSG_NOTICE([serf library configuration via pkg-config]) if test -n "$PKG_CONFIG"; then - for serf_major in serf-2 serf-1; do + for serf_major in serf-2 serf-1 serf; do AC_MSG_CHECKING([for $serf_major library]) if test -n "$serf_prefix" ; then dnl User provided a prefix so we try to find the pc file under diff --git a/subversion/bindings/javahl/native/AuthnCallback.cpp b/subversion/bindings/javahl/native/AuthnCallback.cpp index 3a25e04717653..d0725b6644583 100644 --- a/subversion/bindings/javahl/native/AuthnCallback.cpp +++ b/subversion/bindings/javahl/native/AuthnCallback.cpp @@ -67,6 +67,11 @@ AuthnCallback::ClassImpl::ClassImpl(::Java::Env env, jclass cls) "(Ljava/lang/String;Z)" JAVAHL_ARG("/callback/AuthnCallback") "$SSLClientCertResult;")), + m_mid_ssl_client_cert_uri_prompt( + env.GetMethodID(cls, "sslClientCertUriPrompt", + "(Ljava/lang/String;Z)" + JAVAHL_ARG("/callback/AuthnCallback") + "$SSLClientCertUriResult;")), m_mid_ssl_client_cert_passphrase_prompt( env.GetMethodID(cls, "sslClientCertPassphrasePrompt", "(Ljava/lang/String;Z)" @@ -117,6 +122,15 @@ jobject AuthnCallback::ssl_client_cert_prompt(const ::Java::String& realm.get(), jboolean(may_save)); } +jobject AuthnCallback::ssl_client_cert_uri_prompt( + const ::Java::String& realm, + bool may_save) +{ + return m_env.CallObjectMethod(m_jthis, + impl().m_mid_ssl_client_cert_uri_prompt, + realm.get(), jboolean(may_save)); +} + jobject AuthnCallback::ssl_client_cert_passphrase_prompt( const ::Java::String& realm, bool may_save) diff --git a/subversion/bindings/javahl/native/AuthnCallback.hpp b/subversion/bindings/javahl/native/AuthnCallback.hpp index f30d00475d567..17c0b654deeec 100644 --- a/subversion/bindings/javahl/native/AuthnCallback.hpp +++ b/subversion/bindings/javahl/native/AuthnCallback.hpp @@ -216,6 +216,12 @@ class AuthnCallback : public ::Java::Object */ jobject ssl_client_cert_prompt(const ::Java::String& realm, bool may_save); + /** + * Invokes the Java method AuthnCallback.sslClientCertUriPrompt(). + */ + jobject ssl_client_cert_uri_prompt(const ::Java::String& realm, + bool may_save); + /** * Invokes the Java method AuthnCallback.sslClientCertPassphrasePrompt(). */ @@ -250,6 +256,7 @@ class AuthnCallback : public ::Java::Object const ::Java::MethodID m_mid_user_password_prompt; const ::Java::MethodID m_mid_ssl_server_trust_prompt; const ::Java::MethodID m_mid_ssl_client_cert_prompt; + const ::Java::MethodID m_mid_ssl_client_cert_uri_prompt; const ::Java::MethodID m_mid_ssl_client_cert_passphrase_prompt; const ::Java::MethodID m_mid_allow_store_plaintext_password; const ::Java::MethodID m_mid_allow_store_plaintext_passphrase; diff --git a/subversion/bindings/javahl/native/OperationContext.cpp b/subversion/bindings/javahl/native/OperationContext.cpp index 19c7ea3c4c32e..63d3dd62b74fe 100644 --- a/subversion/bindings/javahl/native/OperationContext.cpp +++ b/subversion/bindings/javahl/native/OperationContext.cpp @@ -181,6 +181,8 @@ OperationContext::getAuthBaton(SVN::Pool &in_pool) APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; svn_auth_get_ssl_client_cert_file_provider(&provider, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + svn_auth_get_ssl_client_cert_uri_provider(&provider, pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; svn_auth_get_ssl_client_cert_pw_file_provider2( &provider, plaintext_passphrase_prompt_func, plaintext_passphrase_prompt_baton, diff --git a/subversion/bindings/javahl/native/Prompter.cpp b/subversion/bindings/javahl/native/Prompter.cpp index 2a1a70497ba8d..fc7dcd0224644 100644 --- a/subversion/bindings/javahl/native/Prompter.cpp +++ b/subversion/bindings/javahl/native/Prompter.cpp @@ -126,6 +126,20 @@ get_provider_client_ssl(SVN::Pool &in_pool) return provider; } +svn_auth_provider_object_t *Prompter:: +get_provider_client_ssl_uri(SVN::Pool &in_pool) +{ + apr_pool_t *pool = in_pool.getPool(); + svn_auth_provider_object_t *provider; + svn_auth_get_ssl_client_cert_uri_prompt_provider(&provider, + ssl_client_cert_uri_prompt, + this, + 2 /* retry limit */, + pool); + + return provider; +} + svn_auth_provider_object_t * Prompter::get_provider_client_ssl_password(SVN::Pool &in_pool) { @@ -205,6 +219,22 @@ svn_error_t *Prompter::ssl_client_cert_prompt( return err; } +svn_error_t *Prompter::ssl_client_cert_uri_prompt( + svn_auth_cred_ssl_client_cert_uri_t **cred_p, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + const ::Java::Env env; + svn_error_t *err; + SVN_JAVAHL_CATCH( + env, SVN_ERR_RA_NOT_AUTHORIZED, + err = static_cast(baton)->dispatch_ssl_client_cert_uri_prompt( + env, cred_p, realm, may_save, pool)); + return err; +} + svn_error_t *Prompter::ssl_client_cert_pw_prompt( svn_auth_cred_ssl_client_cert_pw_t **cred_p, void *baton, @@ -373,6 +403,32 @@ svn_error_t *Prompter::dispatch_ssl_client_cert_prompt( return SVN_NO_ERROR; } +svn_error_t *Prompter::dispatch_ssl_client_cert_uri_prompt( + ::Java::Env env, + svn_auth_cred_ssl_client_cert_uri_t **cred_p, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + ::JavaHL::AuthnCallback authn(env, m_prompter.get()); + + ::JavaHL::AuthnCallback::AuthnResult result( + env, + authn.ssl_client_cert_uri_prompt( + ::Java::String(env, realm), may_save)); + if (!result.get()) + return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, + _("User canceled dialog")); + + ::Java::String uri(env, result.identity()); + svn_auth_cred_ssl_client_cert_uri_t *cred = + static_cast(apr_pcalloc(pool, sizeof(*cred))); + cred->cert_uri = uri.strdup(pool); + cred->may_save = result.save(); + *cred_p = cred; + + return SVN_NO_ERROR; +} svn_error_t *Prompter::dispatch_ssl_client_cert_pw_prompt( ::Java::Env env, diff --git a/subversion/bindings/javahl/native/Prompter.h b/subversion/bindings/javahl/native/Prompter.h index 7782f9ec2698e..d777d28802958 100644 --- a/subversion/bindings/javahl/native/Prompter.h +++ b/subversion/bindings/javahl/native/Prompter.h @@ -61,6 +61,7 @@ class Prompter svn_auth_provider_object_t *get_provider_simple(SVN::Pool &in_pool); svn_auth_provider_object_t *get_provider_server_ssl_trust(SVN::Pool &in_pool); svn_auth_provider_object_t *get_provider_client_ssl(SVN::Pool &in_pool); + svn_auth_provider_object_t *get_provider_client_ssl_uri(SVN::Pool &in_pool); svn_auth_provider_object_t *get_provider_client_ssl_password(SVN::Pool &in_pool); protected: @@ -147,6 +148,20 @@ class Prompter svn_boolean_t may_save, apr_pool_t *pool); + virtual svn_error_t *dispatch_ssl_client_cert_uri_prompt( + ::Java::Env env, + svn_auth_cred_ssl_client_cert_uri_t **cred_p, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool); + + static svn_error_t *ssl_client_cert_uri_prompt( + svn_auth_cred_ssl_client_cert_uri_t **cred_p, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool); + protected: virtual svn_error_t *dispatch_plaintext_prompt( ::Java::Env env, diff --git a/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNConfig.java b/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNConfig.java index eb4e82fc8ac7d..0ba6c56b28258 100644 --- a/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNConfig.java +++ b/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNConfig.java @@ -45,6 +45,7 @@ public interface ISVNConfig public static final String KWALLET_WALLET = "kwallet-wallet"; public static final String KWALLET_SVN_APPLICATION_NAME_WITH_PID = "kwallet-svn-application-name-with-pid"; public static final String SSL_CLIENT_CERT_FILE_PROMPT = "ssl-client-cert-file-prompt"; + public static final String SSL_CLIENT_CERT_URI_PROMPT = "ssl-client-cert-uri-prompt"; public static final String SECTION_HELPERS = "helpers"; public static final String EDITOR_CMD = "editor-cmd"; @@ -101,6 +102,7 @@ public interface ISVNConfig public static final String SSL_TRUST_DEFAULT_CA = "ssl-trust-default-ca"; public static final String SSL_CLIENT_CERT_FILE = "ssl-client-cert-file"; public static final String SSL_CLIENT_CERT_PASSWORD = "ssl-client-cert-password"; + public static final String SSL_CLIENT_CERT_URI = "ssl-client-cert-uri"; public static final String SSL_PKCS11_PROVIDER = "ssl-pkcs11-provider"; public static final String HTTP_LIBRARY = "http-library"; public static final String STORE_PASSWORDS = "store-passwords"; diff --git a/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/AuthnCallback.java b/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/AuthnCallback.java index d763bf2127fce..5889e3004efef 100644 --- a/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/AuthnCallback.java +++ b/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/AuthnCallback.java @@ -439,6 +439,53 @@ public SSLClientCertResult(String path, boolean maySave) sslClientCertPrompt(String realm, boolean maySave); + /** + * The result type used by {@see #sslClientCertUriPrompt}. + */ + public static final class SSLClientCertUriResult + extends AuthnResult + implements java.io.Serializable + { + // Update the serialVersionUID when there is an incompatible change made to + // this class. See the Java documentation (following link or its counter- + // part in your specific Java release) for when a change is incompatible. + // https://docs.oracle.com/en/java/javase/11/docs/specs/serialization/version.html#type-changes-affecting-serialization + private static final long serialVersionUID = 1L; + + /** + * Set the uri of the cerfiticate store in the result. + * Assumes the result may not be stored permanently. + * @param path The uri of the certificate store. + */ + public SSLClientCertUriResult(String uri) + { + identity = uri; + } + + /** + * Set the uri of the cerfiticate store in the result. + * @param uri The uri of the certificate store. + * @param maySave Set if the result may be stored permanently. + */ + public SSLClientCertUriResult(String uri, boolean maySave) + { + save = maySave; + identity = uri; + } + } + + /** + * Ask for the URI of a client SSL certificate. + * @param realm The realm from which the question originates. + * @param maySave Indicates whether saving credentials is allowed; + * if false, the maySave flag + * in the return value will be ignored. + * @return The result, or null if cancelled. + */ + public SSLClientCertUriResult + sslClientCertUriPrompt(String realm, boolean maySave); + + /** * The result type used by {@see #sslClientCertPassphrasePrompt}. */ diff --git a/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNTests.java b/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNTests.java index 098da094afcda..8fac3d024aea3 100644 --- a/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNTests.java +++ b/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNTests.java @@ -374,6 +374,12 @@ private static class DefaultAuthnCallback return null; } + public SSLClientCertUriResult + sslClientCertUriPrompt(String realm, boolean maySave) + { + return null; + } + public SSLClientCertPassphraseResult sslClientCertPassphrasePrompt(String realm, boolean maySave) { diff --git a/subversion/bindings/swig/core.i b/subversion/bindings/swig/core.i index 63d6922274b38..7e0e201519b22 100644 --- a/subversion/bindings/swig/core.i +++ b/subversion/bindings/swig/core.i @@ -256,6 +256,7 @@ %ignore svn_cmdline_auth_plaintext_prompt; %ignore svn_cmdline_auth_simple_prompt; %ignore svn_cmdline_auth_ssl_client_cert_prompt; +%ignore svn_cmdline_auth_ssl_client_cert_uri_prompt; %ignore svn_cmdline_auth_ssl_client_cert_pw_prompt; %ignore svn_cmdline_auth_ssl_server_trust_prompt; %ignore svn_cmdline_auth_username_prompt; @@ -752,6 +753,7 @@ core_set_current_pool (apr_pool_t *pool) %authprompt_callback_typemap(username) %authprompt_callback_typemap(ssl_server_trust) %authprompt_callback_typemap(ssl_client_cert) +%authprompt_callback_typemap(ssl_client_cert_uri) %authprompt_callback_typemap(ssl_client_cert_pw) %authprompt_callback_typemap(gnome_keyring_unlock) @@ -1148,6 +1150,20 @@ svn_swig_rb_auth_get_ssl_client_cert_prompt_provider( return rb_ary_new3(1, (VALUE)prompt_baton); } +static VALUE +svn_swig_rb_auth_get_ssl_client_cert_uri_prompt_provider( + svn_auth_provider_object_t **provider, + svn_auth_ssl_client_cert_uri_prompt_func_t prompt_func, + void *prompt_baton, + int retry_limit, + apr_pool_t *pool) +{ + svn_auth_get_ssl_client_cert_uri_prompt_provider(provider, prompt_func, + prompt_baton, retry_limit, + pool); + return rb_ary_new3(1, (VALUE)prompt_baton); +} + static VALUE svn_swig_rb_auth_get_ssl_client_cert_pw_prompt_provider( svn_auth_provider_object_t **provider, diff --git a/subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.c b/subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.c index e13bc9a94834b..8cbac6264028f 100644 --- a/subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.c +++ b/subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.c @@ -1385,6 +1385,28 @@ svn_error_t *svn_swig_pl_thunk_ssl_client_cert_pw_prompt( return SVN_NO_ERROR; } +/* NOTE: calls back into Perl (by calling svn_swig_pl_callback_thunk) */ +svn_error_t *svn_swig_pl_thunk_ssl_client_cert_uri_prompt( + svn_auth_cred_ssl_client_cert_uri_t **cred, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + /* Be nice and allocate the memory for the cred structure before passing it + * off to the perl space */ + *cred = apr_pcalloc(pool, sizeof(**cred)); + if (!*cred) { + croak("Could not allocate memory for cred structure"); + } + svn_swig_pl_callback_thunk(CALL_SV, + baton, NULL, + "SsbS", *cred, _SWIG_TYPE("svn_auth_cred_ssl_client_cert_uri_t *"), + realm, may_save, pool, POOLINFO); + + return SVN_NO_ERROR; +} + /* Thunked version of svn_wc_notify_func_t callback type */ /* NOTE: calls back into Perl (by calling svn_swig_pl_callback_thunk) */ void svn_swig_pl_notify_func(void * baton, diff --git a/subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h b/subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h index 06abc63de6311..795dc59c4205f 100644 --- a/subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h +++ b/subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h @@ -209,6 +209,14 @@ svn_error_t *svn_swig_pl_thunk_ssl_client_cert_pw_prompt svn_boolean_t may_save, apr_pool_t *pool); +/* thunked ssl_client_cert_uri callback function */ +svn_error_t *svn_swig_pl_thunk_ssl_client_cert_uri_prompt + (svn_auth_cred_ssl_client_cert_uri_t **cred, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool); + /* Thunked version of svn_wc_notify_func_t callback type */ void svn_swig_pl_notify_func(void * baton, const char *path, diff --git a/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c b/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c index 10cf09232ad05..4b57ff1adf83e 100644 --- a/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c +++ b/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c @@ -4414,6 +4414,110 @@ svn_swig_py_auth_ssl_client_cert_pw_prompt_func( return err; } +svn_error_t * +svn_swig_py_auth_ssl_client_cert_uri_prompt_func( + svn_auth_cred_ssl_client_cert_uri_t **cred, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + PyObject *function = baton; + PyObject *result; + svn_auth_cred_ssl_client_cert_uri_t *creds = NULL; + svn_error_t *err = SVN_NO_ERROR; + + if ((function == NULL) || (function == Py_None)) + return SVN_NO_ERROR; + + svn_swig_py_acquire_py_lock(); + + if ((result = PyObject_CallFunction(function, + (char *)SVN_SWIG_BYTES_FMT "lO&", + realm, may_save, + make_ob_pool, pool)) == NULL) + { + err = callback_exception_error(); + } + else + { + if (result != Py_None) + { + svn_auth_cred_ssl_client_cert_uri_t *tmp_creds = NULL; + if (svn_swig_ConvertPtrString + (result, (void **)&tmp_creds, + "svn_auth_cred_ssl_client_cert_uri_t *")) + { + err = type_conversion_error + ("svn_auth_cred_ssl_client_cert_uri_t *"); + } + else + { + creds = apr_pcalloc(pool, sizeof(*creds)); + creds->cert_uri = tmp_creds->cert_uri ? + apr_pstrdup(pool, tmp_creds->cert_uri) : NULL; + creds->may_save = tmp_creds->may_save; + } + } + Py_DECREF(result); + } + svn_swig_py_release_py_lock(); + *cred = creds; + return err; +} + +svn_error_t * +svn_swig_py_auth_ssl_client_cert_uri_prompt_func( + svn_auth_cred_ssl_client_cert_uri_t **cred, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + PyObject *function = baton; + PyObject *result; + svn_auth_cred_ssl_client_cert_uri_t *creds = NULL; + svn_error_t *err = SVN_NO_ERROR; + + if ((function == NULL) || (function == Py_None)) + return SVN_NO_ERROR; + + svn_swig_py_acquire_py_lock(); + + if ((result = PyObject_CallFunction(function, + (char *)SVN_SWIG_BYTES_FMT "lO&", + realm, may_save, + make_ob_pool, pool)) == NULL) + { + err = callback_exception_error(); + } + else + { + if (result != Py_None) + { + svn_auth_cred_ssl_client_cert_uri_t *tmp_creds = NULL; + if (svn_swig_ConvertPtrString + (result, (void **)&tmp_creds, + "svn_auth_cred_ssl_client_cert_uri_t *")) + { + err = type_conversion_error + ("svn_auth_cred_ssl_client_cert_uri_t *"); + } + else + { + creds = apr_pcalloc(pool, sizeof(*creds)); + creds->cert_uri = tmp_creds->cert_uri ? + apr_pstrdup(pool, tmp_creds->cert_uri) : NULL; + creds->may_save = tmp_creds->may_save; + } + } + Py_DECREF(result); + } + svn_swig_py_release_py_lock(); + *cred = creds; + return err; +} + svn_error_t * svn_swig_py_config_auth_walk_func(svn_boolean_t *delete_cred, void *walk_baton, diff --git a/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h b/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h index 6e5afeee0eaa7..5c7980c21069b 100644 --- a/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h +++ b/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h @@ -475,6 +475,20 @@ svn_error_t *svn_swig_py_auth_ssl_client_cert_pw_prompt_func( svn_boolean_t may_save, apr_pool_t *pool); +svn_error_t *svn_swig_py_auth_ssl_client_cert_uri_prompt_func( + svn_auth_cred_ssl_client_cert_uri_t **cred, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool); + +svn_error_t *svn_swig_py_auth_ssl_client_cert_uri_prompt_func( + svn_auth_cred_ssl_client_cert_uri_t **cred, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool); + /* auth cleanup callback */ svn_error_t *svn_swig_py_config_auth_walk_func(svn_boolean_t *delete_cred, void *walk_baton, diff --git a/subversion/bindings/swig/python/tests/auth.py b/subversion/bindings/swig/python/tests/auth.py index eec8ce13fc667..4205e875721bc 100644 --- a/subversion/bindings/swig/python/tests/auth.py +++ b/subversion/bindings/swig/python/tests/auth.py @@ -80,6 +80,18 @@ def myfunc(realm, may_save, pool): core.SVN_AUTH_CRED_SSL_CLIENT_CERT, b"somerealm", baton) self.assertTrue(creds is not None) + def test_credentials_get_ssl_client_cert_uri(self): + def myfunc(realm, may_save, pool): + self.assertEqual(b"somerealm", realm) + ssl_cred = core.svn_auth_cred_ssl_client_cert_uri_t() + ssl_cred.cert_uri = b"my-certs-uri" + ssl_cred.may_save = False + return ssl_cred + baton = core.svn_auth_open([core.svn_auth_get_ssl_client_cert_uri_prompt_provider(myfunc, 1)]) + creds = core.svn_auth_first_credentials( + core.SVN_AUTH_CRED_SSL_CLIENT_CERT_URI, b"somerealm", baton) + self.assertTrue(creds is not None) + def test_credentials_get_ssl_client_cert_pw(self): def myfunc(realm, may_save, pool): self.assertEqual(b"somerealm", realm) diff --git a/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c b/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c index 58145da3a685a..ffb29ec1e6c03 100644 --- a/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c +++ b/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c @@ -3189,6 +3189,48 @@ svn_swig_rb_auth_ssl_client_cert_pw_prompt_func( return err; } +svn_error_t * +svn_swig_rb_auth_ssl_client_cert_uri_prompt_func( + svn_auth_cred_ssl_client_cert_uri_t **cred, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + svn_auth_cred_ssl_client_cert_uri_t *new_cred = NULL; + svn_error_t *err = SVN_NO_ERROR; + VALUE proc, rb_pool; + + svn_swig_rb_from_baton((VALUE)baton, &proc, &rb_pool); + + if (!NIL_P(proc)) { + callback_baton_t cbb; + VALUE result; + + cbb.receiver = proc; + cbb.message = id_call; + cbb.args = rb_ary_new3(2, + c2r_string2(realm), + RTEST(may_save) ? Qtrue : Qfalse); + result = invoke_callback_handle_error((VALUE)(&cbb), rb_pool, &err); + + if (!NIL_P(result)) { + void *result_cred = NULL; + svn_auth_cred_ssl_client_cert_uri_t *tmp_cred = NULL; + + r2c_swig_type2(result, "svn_auth_cred_ssl_client_cert_uri_t *", + &result_cred); + tmp_cred = (svn_auth_cred_ssl_client_cert_uri_t *)result_cred; + new_cred = apr_pcalloc(pool, sizeof(*new_cred)); + new_cred->cert_uri = tmp_cred->cert_uri ? + apr_pstrdup(pool, tmp_cred->cert_uri) : NULL; + new_cred->may_save = tmp_cred->may_save; + } + } + + *cred = new_cred; + return err; +} apr_file_t * svn_swig_rb_make_file(VALUE file, apr_pool_t *pool) diff --git a/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h b/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h index 3dbc3ec565848..8781d593e8a6a 100644 --- a/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h +++ b/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h @@ -345,6 +345,13 @@ svn_error_t *svn_swig_rb_auth_ssl_client_cert_pw_prompt_func( svn_boolean_t may_save, apr_pool_t *pool); +svn_error_t *svn_swig_rb_auth_ssl_client_cert_uri_prompt_func( + svn_auth_cred_ssl_client_cert_uri_t **cred, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool); + apr_file_t *svn_swig_rb_make_file(VALUE file, apr_pool_t *pool); svn_stream_t *svn_swig_rb_make_stream(VALUE io); diff --git a/subversion/include/svn_auth.h b/subversion/include/svn_auth.h index aa1da3abb3f0d..25f3ea0cd1fb9 100644 --- a/subversion/include/svn_auth.h +++ b/subversion/include/svn_auth.h @@ -248,6 +248,32 @@ typedef struct svn_auth_cred_ssl_client_cert_t } svn_auth_cred_ssl_client_cert_t; +/** SSL client certificate url credential type. + * + * The following auth parameters are available to the providers: + * + * - @c SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS (@c svn_config_t*) + * - @c SVN_AUTH_PARAM_SERVER_GROUP (@c char*) + * + * The following optional auth parameters are relevant to the providers: + * + * - @c SVN_AUTH_PARAM_NO_AUTH_CACHE (@c void*) + */ +#define SVN_AUTH_CRED_SSL_CLIENT_CERT_URI "svn.ssl.client-uri" + +/** @c SVN_AUTH_CRED_SSL_CLIENT_CERT_URI credentials. */ +typedef struct svn_auth_cred_ssl_client_cert_uri_t +{ + /** URI to the certificate store */ + const char *cert_uri; + /** Indicates if the credentials may be saved (to disk). For example, a + * GUI prompt implementation with a remember certificate checkbox shall + * set @a may_save to TRUE if the checkbox is checked. + */ + svn_boolean_t may_save; +} svn_auth_cred_ssl_client_cert_uri_t; + + /** A function returning an SSL client certificate passphrase provider. */ typedef void (*svn_auth_ssl_client_cert_pw_provider_func_t)( svn_auth_provider_object_t **provider, @@ -464,6 +490,24 @@ typedef svn_error_t *(*svn_auth_ssl_client_cert_prompt_func_t)( apr_pool_t *pool); +/** Set @a *cred by prompting the user, allocating @a *cred in @a pool. + * @a baton is an implementation-specific closure. @a realm is a string + * that can be used in the prompt string. + * + * If @a may_save is FALSE, the auth system does not allow the credentials + * to be saved (to disk). A prompt function shall not ask the user if the + * credentials shall be saved if @a may_save is FALSE. For example, a GUI + * client with a remember certificate checkbox would grey out the checkbox + * if @a may_save is FALSE. + */ +typedef svn_error_t *(*svn_auth_ssl_client_cert_uri_prompt_func_t)( + svn_auth_cred_ssl_client_cert_uri_t **cred, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool); + + /** Set @a *cred by prompting the user, allocating @a *cred in @a pool. * @a baton is an implementation-specific closure. @a realm is a string * identifying the certificate, and can be used in the prompt string. @@ -1219,6 +1263,21 @@ svn_auth_get_ssl_client_cert_file_provider( apr_pool_t *pool); +/** Set @a *provider to an authentication provider of type @c + * svn_auth_cred_ssl_client_cert_uri_t, allocated in @a pool. + * + * @a *provider retrieves its credentials from the configuration + * mechanism. The returned credential is used to load the appropriate + * client certificate for authentication when requested by a server. + * + * @since New in 1.15. + */ +void +svn_auth_get_ssl_client_cert_uri_provider( + svn_auth_provider_object_t **provider, + apr_pool_t *pool); + + /** Set @a *provider to an authentication provider of type @c * svn_auth_cred_ssl_client_cert_pw_t that gets/sets information from the user's * ~/.subversion configuration directory. @@ -1300,6 +1359,26 @@ svn_auth_get_ssl_client_cert_prompt_provider( apr_pool_t *pool); +/** Set @a *provider to an authentication provider of type @c + * svn_auth_cred_ssl_client_cert_uri_t, allocated in @a pool. + * + * @a *provider retrieves its credentials by using the @a prompt_func + * and @a prompt_baton. The returned credential is used to load the + * appropriate client certificate for authentication when requested by + * a server. The prompt will be retried @a retry_limit times. For + * infinite retries, set @a retry_limit to value less than 0. + * + * @since New in 1.15. + */ +void +svn_auth_get_ssl_client_cert_uri_prompt_provider( + svn_auth_provider_object_t **provider, + svn_auth_ssl_client_cert_uri_prompt_func_t prompt_func, + void *prompt_baton, + int retry_limit, + apr_pool_t *pool); + + /** Set @a *provider to an authentication provider of type @c * svn_auth_cred_ssl_client_cert_pw_t, allocated in @a pool. * diff --git a/subversion/include/svn_cmdline.h b/subversion/include/svn_cmdline.h index 71e61c62e0c26..20b5d2bf34793 100644 --- a/subversion/include/svn_cmdline.h +++ b/subversion/include/svn_cmdline.h @@ -288,6 +288,26 @@ svn_cmdline_auth_ssl_client_cert_pw_prompt( svn_boolean_t may_save, apr_pool_t *pool); + +/** An implementation of @c svn_auth_ssl_client_cert_uri_prompt_func_t that + * prompts the user for the URI of their SSL client certificate via + * the command line. + * + * Records URI of the SSL client certificate store. + * + * @since New in 1.15. + * + * Expects a @c svn_cmdline_prompt_baton_t to be passed as @a baton. + */ +svn_error_t * +svn_cmdline_auth_ssl_client_cert_uri_prompt( + svn_auth_cred_ssl_client_cert_uri_t **cred_p, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool); + + /** An implementation of @c svn_auth_plaintext_prompt_func_t that * prompts the user whether storing unencrypted passwords to disk is OK. * diff --git a/subversion/include/svn_config.h b/subversion/include/svn_config.h index 94e036660da33..c8a62b25eb2d5 100644 --- a/subversion/include/svn_config.h +++ b/subversion/include/svn_config.h @@ -90,6 +90,8 @@ typedef struct svn_config_t svn_config_t; #define SVN_CONFIG_OPTION_SSL_TRUST_DEFAULT_CA "ssl-trust-default-ca" #define SVN_CONFIG_OPTION_SSL_CLIENT_CERT_FILE "ssl-client-cert-file" #define SVN_CONFIG_OPTION_SSL_CLIENT_CERT_PASSWORD "ssl-client-cert-password" +/** @since New in 1.15. */ +#define SVN_CONFIG_OPTION_SSL_CLIENT_CERT_URI "ssl-client-cert-uri" /** @deprecated Not used since 1.8. * @since New in 1.5. */ #define SVN_CONFIG_OPTION_SSL_PKCS11_PROVIDER "ssl-pkcs11-provider" @@ -129,6 +131,8 @@ typedef struct svn_config_t svn_config_t; #define SVN_CONFIG_OPTION_KWALLET_SVN_APPLICATION_NAME_WITH_PID "kwallet-svn-application-name-with-pid" /** @since New in 1.8. */ #define SVN_CONFIG_OPTION_SSL_CLIENT_CERT_FILE_PROMPT "ssl-client-cert-file-prompt" +/** @since New in 1.15. */ +#define SVN_CONFIG_OPTION_SSL_CLIENT_CERT_URI_PROMPT "ssl-client-cert-uri-prompt" /* The majority of options of the "auth" section * has been moved to SVN_CONFIG_CATEGORY_SERVERS. */ #define SVN_CONFIG_SECTION_HELPERS "helpers" diff --git a/subversion/libsvn_ra_serf/ra_serf.h b/subversion/libsvn_ra_serf/ra_serf.h index eace1af5319ac..dcf21bcce1acb 100644 --- a/subversion/libsvn_ra_serf/ra_serf.h +++ b/subversion/libsvn_ra_serf/ra_serf.h @@ -350,7 +350,7 @@ svn_ra_serf__conn_closed(serf_connection_t *conn, apr_pool_t *pool); -/* Helper function to provide SSL client certificates. +/* Helper function to provide SSL client certificates by path. * * NOTE: This function sets the session's 'pending_error' member when * returning an non-success status. @@ -359,6 +359,15 @@ apr_status_t svn_ra_serf__handle_client_cert(void *data, const char **cert_path); +/* Helper function to provide SSL client certificates by uri. + * + * NOTE: This function sets the session's 'pending_error' member when + * returning an non-success status. + */ +apr_status_t +svn_ra_serf__handle_client_cert_uri(void *data, + const char **cert_uri); + /* Helper function to provide SSL client certificate passwords. * * NOTE: This function sets the session's 'pending_error' member when diff --git a/subversion/libsvn_ra_serf/util.c b/subversion/libsvn_ra_serf/util.c index e010f82d62110..7a412089551b0 100644 --- a/subversion/libsvn_ra_serf/util.c +++ b/subversion/libsvn_ra_serf/util.c @@ -539,7 +539,12 @@ conn_setup(apr_socket_t *sock, serf_ssl_client_cert_provider_set(conn->ssl_context, svn_ra_serf__handle_client_cert, conn, conn->session->pool); - serf_ssl_client_cert_password_set(conn->ssl_context, +#ifdef HAVE_SERF_SSL_CERT_URI_SET + serf_ssl_cert_uri_set(conn->ssl_context, + svn_ra_serf__handle_client_cert_uri, + conn, conn->session->pool); +#endif + serf_ssl_client_cert_password_set(conn->ssl_context, svn_ra_serf__handle_client_cert_pw, conn, conn->session->pool); serf_ssl_server_cert_callback_set(conn->ssl_context, @@ -725,6 +730,60 @@ apr_status_t svn_ra_serf__handle_client_cert(void *data, return save_error(session, err); } +/* Implementation of svn_ra_serf__handle_client_cert_uri */ +static svn_error_t * +handle_client_cert_uri(void *data, + const char **cert_uri, + apr_pool_t *pool) +{ + svn_ra_serf__connection_t *conn = data; + svn_ra_serf__session_t *session = conn->session; + const char *realm; + void *creds; + + *cert_uri = NULL; + + realm = construct_realm(session, session->pool); + + if (!conn->ssl_client_auth_state) + { + SVN_ERR(svn_auth_first_credentials(&creds, + &conn->ssl_client_auth_state, + SVN_AUTH_CRED_SSL_CLIENT_CERT_URI, + realm, + session->auth_baton, + pool)); + } + else + { + SVN_ERR(svn_auth_next_credentials(&creds, + conn->ssl_client_auth_state, + session->pool)); + } + + if (creds) + { + svn_auth_cred_ssl_client_cert_uri_t *client_creds; + client_creds = creds; + *cert_uri = client_creds->cert_uri; + } + + return SVN_NO_ERROR; +} + +/* Implements serf_ssl_need_cert_uri_t for handle_client_cert_uri */ +apr_status_t svn_ra_serf__handle_client_cert_uri(void *data, + const char **cert_uri) +{ + svn_ra_serf__connection_t *conn = data; + svn_ra_serf__session_t *session = conn->session; + svn_error_t *err; + + err = svn_error_trace(handle_client_cert_uri(data, cert_uri, session->pool)); + + return save_error(session, err); +} + /* Implementation for svn_ra_serf__handle_client_cert_pw */ static svn_error_t * handle_client_cert_pw(void *data, diff --git a/subversion/libsvn_subr/cmdline.c b/subversion/libsvn_subr/cmdline.c index 8d7b1d23b5406..1d7c1a023dece 100644 --- a/subversion/libsvn_subr/cmdline.c +++ b/subversion/libsvn_subr/cmdline.c @@ -648,6 +648,9 @@ svn_cmdline_create_auth_baton2(svn_auth_baton_t **ab, svn_auth_get_ssl_client_cert_file_provider(&provider, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + svn_auth_get_ssl_client_cert_uri_provider(&provider, pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + if (!non_interactive) { /* This provider doesn't prompt the user in order to get creds; @@ -665,8 +668,14 @@ svn_cmdline_create_auth_baton2(svn_auth_baton_t **ab, if (!non_interactive) { + svn_boolean_t ssl_client_cert_uri_prompt; svn_boolean_t ssl_client_cert_file_prompt; + SVN_ERR(svn_config_get_bool(cfg, &ssl_client_cert_uri_prompt, + SVN_CONFIG_SECTION_AUTH, + SVN_CONFIG_OPTION_SSL_CLIENT_CERT_URI_PROMPT, + FALSE)); + SVN_ERR(svn_config_get_bool(cfg, &ssl_client_cert_file_prompt, SVN_CONFIG_SECTION_AUTH, SVN_CONFIG_OPTION_SSL_CLIENT_CERT_FILE_PROMPT, @@ -694,6 +703,15 @@ svn_cmdline_create_auth_baton2(svn_auth_baton_t **ab, (&provider, svn_cmdline_auth_ssl_client_cert_pw_prompt, pb, 2, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + /* If configuration allows, add a provider for client-cert uri + prompting, too. */ + if (ssl_client_cert_uri_prompt) + { + svn_auth_get_ssl_client_cert_uri_prompt_provider + (&provider, svn_cmdline_auth_ssl_client_cert_uri_prompt, pb, 2, pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + } + /* If configuration allows, add a provider for client-cert path prompting, too. */ if (ssl_client_cert_file_prompt) diff --git a/subversion/libsvn_subr/config_auth.c b/subversion/libsvn_subr/config_auth.c index 7461b912a7326..f3878e744e10f 100644 --- a/subversion/libsvn_subr/config_auth.c +++ b/subversion/libsvn_subr/config_auth.c @@ -169,6 +169,7 @@ svn_config_walk_auth_data(const char *config_dir, SVN_AUTH_CRED_SIMPLE, SVN_AUTH_CRED_USERNAME, SVN_AUTH_CRED_SSL_CLIENT_CERT, + SVN_AUTH_CRED_SSL_CLIENT_CERT_URI, SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, SVN_AUTH_CRED_SSL_SERVER_TRUST, NULL diff --git a/subversion/libsvn_subr/prompt.c b/subversion/libsvn_subr/prompt.c index 3731d1d6fbab6..62ba5770e5b03 100644 --- a/subversion/libsvn_subr/prompt.c +++ b/subversion/libsvn_subr/prompt.c @@ -775,6 +775,32 @@ svn_cmdline_auth_ssl_client_cert_prompt } +/* This implements 'svn_auth_ssl_client_cert_uri_prompt_func_t'. */ +svn_error_t * +svn_cmdline_auth_ssl_client_cert_uri_prompt + (svn_auth_cred_ssl_client_cert_uri_t **cred_p, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + svn_auth_cred_ssl_client_cert_uri_t *cred = NULL; + const char *cert_uri = NULL; + svn_cmdline_prompt_baton2_t *pb = baton; + + SVN_ERR(maybe_print_realm(realm, pool)); + SVN_ERR(prompt(&cert_uri, _("Client certificate URI: "), + FALSE, pb, pool)); + + cred = apr_palloc(pool, sizeof(*cred)); + cred->cert_uri = cert_uri; + cred->may_save = may_save; + *cred_p = cred; + + return SVN_NO_ERROR; +} + + /* This implements 'svn_auth_ssl_client_cert_pw_prompt_func_t'. */ svn_error_t * svn_cmdline_auth_ssl_client_cert_pw_prompt diff --git a/subversion/libsvn_subr/ssl_client_cert_uri_providers.c b/subversion/libsvn_subr/ssl_client_cert_uri_providers.c new file mode 100644 index 0000000000000..fb7bb998f2f83 --- /dev/null +++ b/subversion/libsvn_subr/ssl_client_cert_uri_providers.c @@ -0,0 +1,208 @@ +/* + * ssl_client_cert_uri_providers.c: providers for + * SVN_AUTH_CRED_SSL_CLIENT_CERT_URI + * + * ==================================================================== + * 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. + * ==================================================================== + */ + +/* ==================================================================== */ + + + +/*** Includes. ***/ + +#include +#include "svn_hash.h" +#include "svn_auth.h" +#include "svn_error.h" +#include "svn_config.h" + + +/*-----------------------------------------------------------------------*/ +/* URI provider */ +/*-----------------------------------------------------------------------*/ + +/* retrieve and load the ssl client certificate uri from servers + config */ +static svn_error_t * +ssl_client_cert_uri_first_credentials(void **credentials_p, + void **iter_baton, + void *provider_baton, + apr_hash_t *parameters, + const char *realmstring, + apr_pool_t *pool) +{ + svn_config_t *cfg = svn_hash_gets(parameters, + SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS); + const char *server_group = svn_hash_gets(parameters, + SVN_AUTH_PARAM_SERVER_GROUP); + const char *cert_uri; + + cert_uri = + svn_config_get_server_setting(cfg, server_group, + SVN_CONFIG_OPTION_SSL_CLIENT_CERT_URI, + NULL); + + if (cert_uri != NULL) + { + svn_auth_cred_ssl_client_cert_uri_t *cred = + apr_palloc(pool, sizeof(*cred)); + + cred->cert_uri = cert_uri; + cred->may_save = FALSE; + *credentials_p = cred; + } + else + { + *credentials_p = NULL; + } + + *iter_baton = NULL; + return SVN_NO_ERROR; +} + + +static const svn_auth_provider_t ssl_client_cert_uri_provider = { + SVN_AUTH_CRED_SSL_CLIENT_CERT_URI, + ssl_client_cert_uri_first_credentials, + NULL, + NULL +}; + + +/*** Public API to SSL uri providers. ***/ +void svn_auth_get_ssl_client_cert_uri_provider + (svn_auth_provider_object_t **provider, apr_pool_t *pool) +{ + svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); + po->vtable = &ssl_client_cert_uri_provider; + *provider = po; +} + + +/*-----------------------------------------------------------------------*/ +/* Prompt provider */ +/*-----------------------------------------------------------------------*/ + +/* Baton type for prompting to send client ssl creds. + There is no iteration baton type. */ +typedef struct ssl_client_cert_uri_prompt_provider_baton_t +{ + svn_auth_ssl_client_cert_uri_prompt_func_t prompt_func; + void *prompt_baton; + + /* how many times to re-prompt after the first one fails */ + int retry_limit; +} ssl_client_cert_uri_prompt_provider_baton_t; + +/* Iteration baton. */ +typedef struct ssl_client_cert_uri_prompt_iter_baton_t +{ + /* The original provider baton */ + ssl_client_cert_uri_prompt_provider_baton_t *pb; + + /* The original realmstring */ + const char *realmstring; + + /* how many times we've reprompted */ + int retries; +} ssl_client_cert_uri_prompt_iter_baton_t; + + +static svn_error_t * +ssl_client_cert_uri_prompt_first_cred(void **credentials_p, + void **iter_baton, + void *provider_baton, + apr_hash_t *parameters, + const char *realmstring, + apr_pool_t *pool) +{ + ssl_client_cert_uri_prompt_provider_baton_t *pb = provider_baton; + ssl_client_cert_uri_prompt_iter_baton_t *ib = + apr_pcalloc(pool, sizeof(*ib)); + const char *no_auth_cache = svn_hash_gets(parameters, + SVN_AUTH_PARAM_NO_AUTH_CACHE); + + SVN_ERR(pb->prompt_func((svn_auth_cred_ssl_client_cert_uri_t **) credentials_p, + pb->prompt_baton, realmstring, ! no_auth_cache, + pool)); + + ib->pb = pb; + ib->realmstring = apr_pstrdup(pool, realmstring); + ib->retries = 0; + *iter_baton = ib; + + return SVN_NO_ERROR; +} + + +static svn_error_t * +ssl_client_cert_uri_prompt_next_cred(void **credentials_p, + void *iter_baton, + void *provider_baton, + apr_hash_t *parameters, + const char *realmstring, + apr_pool_t *pool) +{ + ssl_client_cert_uri_prompt_iter_baton_t *ib = iter_baton; + const char *no_auth_cache = svn_hash_gets(parameters, + SVN_AUTH_PARAM_NO_AUTH_CACHE); + + if ((ib->pb->retry_limit >= 0) && (ib->retries >= ib->pb->retry_limit)) + { + /* give up, go on to next provider. */ + *credentials_p = NULL; + return SVN_NO_ERROR; + } + ib->retries++; + + return ib->pb->prompt_func((svn_auth_cred_ssl_client_cert_uri_t **) + credentials_p, ib->pb->prompt_baton, + ib->realmstring, ! no_auth_cache, pool); +} + + +static const svn_auth_provider_t ssl_client_cert_uri_prompt_provider = { + SVN_AUTH_CRED_SSL_CLIENT_CERT_URI, + ssl_client_cert_uri_prompt_first_cred, + ssl_client_cert_uri_prompt_next_cred, + NULL +}; + + +/*** Public API to SSL prompting providers. ***/ +void svn_auth_get_ssl_client_cert_uri_prompt_provider + (svn_auth_provider_object_t **provider, + svn_auth_ssl_client_cert_uri_prompt_func_t prompt_func, + void *prompt_baton, + int retry_limit, + apr_pool_t *pool) +{ + svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); + ssl_client_cert_uri_prompt_provider_baton_t *pb = apr_palloc(pool, sizeof(*pb)); + + pb->prompt_func = prompt_func; + pb->prompt_baton = prompt_baton; + pb->retry_limit = retry_limit; + + po->vtable = &ssl_client_cert_uri_prompt_provider; + po->provider_baton = pb; + *provider = po; +}