You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@subversion.apache.org by Graham Leggett via dev <de...@subversion.apache.org> on 2025/06/27 09:29:28 UTC
[Patch] Native Windows / PKCS11 smartcard support
Hi all,
The patch below adds native Windows support and PKCS11 smartcard support to the subversion client. Look mom, I just logged in with a smartcard:
Little-Net-8818:~ minfrin$ /tmp/httpd-trunk/bin/svn info https://svn.example.com/svn/example/core/
Passphrase for 'pkcs11:token=Venus%20%28Smartcard%20PIN%29;': ****
Path: core
URL: https://svn.example.com/svn/example/core
Relative URL: ^/
Repository Root: https://svn.example.com/svn/example/core
Repository UUID: 1cd1716d-5548-49d3-a690-118a38ff0867
Revision: 336
Node Kind: directory
Last Changed Author: minfrin@sharp.fm
Last Changed Rev: 336
Last Changed Date: 2025-06-12 15:02:05 +0100 (Thu, 12 Jun 2025)
- Adds the config option ssl-client-cert-uri to specify the URI of a certificate store.
- OpenSSL URIs point at files, or pkcs11 smartcards, or TPMs, or native platform certificate stores.
- Maintains existing ssl-client-cert-file as fallback.
- Depends on URI support in Serf: https://github.com/apache/serf/pull/8
Example configuration:
[apachegroup]
ssl-trust-default-ca = yes
ssl-client-cert-uri = /home/minfrin/.my-cert.p12
ssl-client-cert-password = supersecret
Smartcard example:
[apachegroup]
ssl-trust-default-ca = yes
ssl-client-cert-uri = pkcs11:token=Venus%20%28Smartcard%20PIN%29;
ssl-client-cert-password-prompt = yes
Windows example:
[apachegroup]
ssl-trust-default-ca = yes
ssl-client-cert-uri = cng://MY@currentuser
Regards,
Graham
--
Index: subversion/bindings/javahl/native/AuthnCallback.cpp
===================================================================
--- subversion/bindings/javahl/native/AuthnCallback.cpp (revision 1926739)
+++ subversion/bindings/javahl/native/AuthnCallback.cpp (working copy)
@@ -67,6 +67,11 @@
"(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 @@
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)
Index: subversion/bindings/javahl/native/AuthnCallback.hpp
===================================================================
--- subversion/bindings/javahl/native/AuthnCallback.hpp (revision 1926739)
+++ subversion/bindings/javahl/native/AuthnCallback.hpp (working copy)
@@ -217,6 +217,12 @@
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().
*/
jobject ssl_client_cert_passphrase_prompt(const ::Java::String& realm,
@@ -250,6 +256,7 @@
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;
Index: subversion/bindings/javahl/native/OperationContext.cpp
===================================================================
--- subversion/bindings/javahl/native/OperationContext.cpp (revision 1926739)
+++ subversion/bindings/javahl/native/OperationContext.cpp (working copy)
@@ -181,6 +181,8 @@
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,
Index: subversion/bindings/javahl/native/Prompter.cpp
===================================================================
--- subversion/bindings/javahl/native/Prompter.cpp (revision 1926739)
+++ subversion/bindings/javahl/native/Prompter.cpp (working copy)
@@ -126,6 +126,20 @@
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 @@
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<Prompter*>(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,7 +403,33 @@
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<svn_auth_cred_ssl_client_cert_uri_t*>(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,
svn_auth_cred_ssl_client_cert_pw_t **cred_p,
Index: subversion/bindings/javahl/native/Prompter.h
===================================================================
--- subversion/bindings/javahl/native/Prompter.h (revision 1926739)
+++ subversion/bindings/javahl/native/Prompter.h (working copy)
@@ -61,6 +61,7 @@
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 @@
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,
Index: subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNConfig.java
===================================================================
--- subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNConfig.java (revision 1926739)
+++ subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNConfig.java (working copy)
@@ -45,6 +45,7 @@
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 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";
Index: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/AuthnCallback.java
===================================================================
--- subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/AuthnCallback.java (revision 1926739)
+++ subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/AuthnCallback.java (working copy)
@@ -440,6 +440,53 @@
/**
+ * 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 <code>false</code>, the <code>maySave</code> flag
+ * in the return value will be ignored.
+ * @return The result, or <code>null</code> if cancelled.
+ */
+ public SSLClientCertUriResult
+ sslClientCertUriPrompt(String realm, boolean maySave);
+
+
+ /**
* The result type used by {@see #sslClientCertPassphrasePrompt}.
*/
public static final class SSLClientCertPassphraseResult
Index: subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNTests.java
===================================================================
--- subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNTests.java (revision 1926739)
+++ subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNTests.java (working copy)
@@ -374,6 +374,12 @@
return null;
}
+ public SSLClientCertUriResult
+ sslClientCertUriPrompt(String realm, boolean maySave)
+ {
+ return null;
+ }
+
public SSLClientCertPassphraseResult
sslClientCertPassphrasePrompt(String realm, boolean maySave)
{
Index: subversion/bindings/swig/core.i
===================================================================
--- subversion/bindings/swig/core.i (revision 1926739)
+++ subversion/bindings/swig/core.i (working copy)
@@ -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 @@
%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)
@@ -1149,6 +1151,20 @@
}
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,
svn_auth_ssl_client_cert_pw_prompt_func_t prompt_func,
Index: subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.c
===================================================================
--- subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.c (revision 1926739)
+++ subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.c (working copy)
@@ -1385,6 +1385,28 @@
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,
Index: subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h
===================================================================
--- subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h (revision 1926739)
+++ subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h (working copy)
@@ -209,6 +209,14 @@
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,
Index: subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c
===================================================================
--- subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c (revision 1926739)
+++ subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c (working copy)
@@ -4415,6 +4415,58 @@
}
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,
const char *cred_kind,
Index: subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h
===================================================================
--- subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h (revision 1926739)
+++ subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h (working copy)
@@ -475,6 +475,13 @@
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,
Index: subversion/bindings/swig/python/tests/auth.py
===================================================================
--- subversion/bindings/swig/python/tests/auth.py (revision 1926739)
+++ subversion/bindings/swig/python/tests/auth.py (working copy)
@@ -80,6 +80,18 @@
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)
Index: subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c
===================================================================
--- subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c (revision 1926739)
+++ subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c (working copy)
@@ -3189,7 +3189,49 @@
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)
{
Index: subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h
===================================================================
--- subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h (revision 1926739)
+++ subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h (working copy)
@@ -345,6 +345,13 @@
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);
Index: subversion/include/svn_auth.h
===================================================================
--- subversion/include/svn_auth.h (revision 1926739)
+++ subversion/include/svn_auth.h (working copy)
@@ -248,6 +248,32 @@
} 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,
@@ -466,6 +492,24 @@
/** 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.
*
* If @a may_save is FALSE, the auth system does not allow the credentials
@@ -1220,6 +1264,21 @@
/** 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.
*
@@ -1301,6 +1360,26 @@
/** 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.
*
* @a *provider retrieves its credentials by using the @a prompt_func
Index: subversion/include/svn_cmdline.h
===================================================================
--- subversion/include/svn_cmdline.h (revision 1926739)
+++ subversion/include/svn_cmdline.h (working copy)
@@ -288,6 +288,26 @@
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.
*
Index: subversion/include/svn_config.h
===================================================================
--- subversion/include/svn_config.h (revision 1926739)
+++ subversion/include/svn_config.h (working copy)
@@ -90,6 +90,8 @@
#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 @@
#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"
Index: subversion/libsvn_ra_serf/ra_serf.h
===================================================================
--- subversion/libsvn_ra_serf/ra_serf.h (revision 1926739)
+++ subversion/libsvn_ra_serf/ra_serf.h (working copy)
@@ -350,7 +350,7 @@
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 @@
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
Index: subversion/libsvn_ra_serf/util.c
===================================================================
--- subversion/libsvn_ra_serf/util.c (revision 1926739)
+++ subversion/libsvn_ra_serf/util.c (working copy)
@@ -539,6 +539,9 @@
serf_ssl_client_cert_provider_set(conn->ssl_context,
svn_ra_serf__handle_client_cert,
conn, conn->session->pool);
+ serf_ssl_cert_uri_set(conn->ssl_context,
+ svn_ra_serf__handle_client_cert_uri,
+ conn, conn->session->pool);
serf_ssl_client_cert_password_set(conn->ssl_context,
svn_ra_serf__handle_client_cert_pw,
conn, conn->session->pool);
@@ -725,6 +728,60 @@
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,
Index: subversion/libsvn_subr/cmdline.c
===================================================================
--- subversion/libsvn_subr/cmdline.c (revision 1926739)
+++ subversion/libsvn_subr/cmdline.c (working copy)
@@ -648,6 +648,9 @@
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 @@
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 @@
(&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)
Index: subversion/libsvn_subr/config_auth.c
===================================================================
--- subversion/libsvn_subr/config_auth.c (revision 1926739)
+++ subversion/libsvn_subr/config_auth.c (working copy)
@@ -169,6 +169,7 @@
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
Index: subversion/libsvn_subr/prompt.c
===================================================================
--- subversion/libsvn_subr/prompt.c (revision 1926739)
+++ subversion/libsvn_subr/prompt.c (working copy)
@@ -775,6 +775,32 @@
}
+/* 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
Index: subversion/libsvn_subr/ssl_client_cert_uri_providers.c
===================================================================
--- subversion/libsvn_subr/ssl_client_cert_uri_providers.c (nonexistent)
+++ subversion/libsvn_subr/ssl_client_cert_uri_providers.c (working copy)
@@ -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 <apr_pools.h>
+#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;
+}
Property changes on: subversion/libsvn_subr/ssl_client_cert_uri_providers.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property