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