[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

[PATCH] Fix issue #1330: accept server cert permanently, take 3

From: Tobias Ringström <tobias_at_ringstrom.mine.nu>
Date: 2003-09-12 02:56:06 CEST

Here is the third iteration of the solution to issue #1330. The changes
from take 2 are:

   1. Added the server config option ssl-ignore-invalid-date back in, as
      well as the new ssl-override-cert-hostname option.
   2. Permit the user to trust the certificate permanently even if there
      are hostname and date problems. If the certificate has hostname or
      date issues, the user will still be prompted for future connections.
   3. Removed the neon dependancy from the command line client by
      passing the required certificate info as strings to the prompt
      function.

Updated log message and patch are attached. Enjoy!

/Tobias

Log message:
Fix issue #1330: accept server cert permanently

User visible changes
====================

If the server cert is signed by an unknown CA, the user can choose to
reject the cert, to accept it temporarily or to accept it permanently.
If the server certificate has other problems (incorrect hostname, not
yet valid or expired), the user will still be prompted for each
connection. Subversion now behaves in the same way as Mozilla and
other web browsers.

The ssl-ignore-unknown-ca server configuration option is now gone
because it is no longer needed.

The ssl-ignore-host-mismatch server configuration option is replaced
by the new option ssl-override-cert-hostname which takes a single host
name.

Implementation details
======================

See the implementation proposal in notes/server-cert-revamp.txt.

subversion/include/svn_config.h
  Removed ssl-ignore-unknown-ca and ssl-ignore-host-mismatch.
  Added ssl-override-cert-hostname.

subversion/include/svn_client.h
  Add comments.

subversion/include/svn_auth.h
  (svn_auth_cred_server_ssl_t): Replace failures_allow with
  trust_permanantly.
  (svn_auth_ssl_server_cert_info_t): New struct to hold the server
  certificate info.
  (svn_auth_ssl_server_prompt_func_t): Add the cert info struct to
  the server certificate prompt function prototype.

subversion/libsvn_subr/config_file.c
  (ensure_auth_dirs): Create the SVN_AUTH_CRED_SERVER_SSL auth dir.
  (svn_config_ensure): Remove ssl-ignore-unknown-ca and
  ssl-ignore-host-mismatch config options from the
  template servers file, and add ssl-override-cert-hostname.

subversion/libsvn_client/auth.c
  (ssl_server_file_provider_baton_t): New type
  (server_ssl_file_first_credentials): Rewritten to look for the cert
  in the auth system and check for server config options.
  (server_ssl_file_save_credentials): New function used to store a
  certificate in the auth system.
  (svn_client_get_ssl_server_file_provider): Allocate a provider baton
  and initialize the provider type in the baton.
  (server_ssl_prompt_first_cred): Pass the certificate info struct to
  the prompt function.

subversion/clients/cmdline/cl.h
  (svn_cl__auth_ssl_server_prompt): Add the certificate info struct to
  the prompt prototype.

subversion/clients/cmdline/prompt.c
  (svn_cl__auth_ssl_server_prompt): Rewritten to present an
  informative prompt to the user, and create the appropriate
  credentials based on the user's response.

subversion/libsvn_ra_dav/session.c
  (server_ssl_callback): Export the certificate information and put it
  in the auth_baton parameter hash. Use the base-64 encoded DER of the
  certificate as the realm string. Save credentials permanently if
  requested.

Index: subversion/include/svn_config.h
===================================================================
--- subversion/include/svn_config.h (revision 7051)
+++ subversion/include/svn_config.h (working copy)
@@ -63,9 +63,8 @@
 #define SVN_CONFIG_OPTION_NEON_DEBUG_MASK "neon-debug-mask"
 #define SVN_CONFIG_OPTION_SSL_AUTHORITY_FILES "ssl-authority-files"
 #define SVN_CONFIG_OPTION_SSL_TRUST_DEFAULT_CA "ssl-trust-default-ca"
-#define SVN_CONFIG_OPTION_SSL_IGNORE_UNKNOWN_CA "ssl-ignore-unknown-ca"
 #define SVN_CONFIG_OPTION_SSL_IGNORE_INVALID_DATE "ssl-ignore-invalid-date"
-#define SVN_CONFIG_OPTION_SSL_IGNORE_HOST_MISMATCH "ssl-ignore-host-mismatch"
+#define SVN_CONFIG_OPTION_SSL_OVERRIDE_CERT_HSTNAME "ssl-override-cert-hostname"
 #define SVN_CONFIG_OPTION_SSL_CLIENT_CERT_FILE "ssl-client-cert-file"
 #define SVN_CONFIG_OPTION_SSL_CLIENT_CERT_PASSWORD "ssl-client-cert-password"
 
Index: subversion/include/svn_client.h
===================================================================
--- subversion/include/svn_client.h (revision 7051)
+++ subversion/include/svn_client.h (working copy)
@@ -151,7 +151,10 @@
  * (@c SVN_AUTH_PARAM_SERVER_GROUP)
  *
  * - the failure bitmask reported by the ssl certificate validator
- * (@c SVN_AUTH_PARAM_SSL_SERVER_FAILURES_IN)
+ * (@c SVN_AUTH_PARAM_SSL_SERVER_FAILURES)
+ *
+ * - the certificate info (svn_auth_ssl_server_cert_info_t*)
+ * (@c SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO)
  */
 void
 svn_client_get_ssl_server_file_provider (svn_auth_provider_object_t **provider,
@@ -208,7 +211,10 @@
  * the @c auth_baton:
  *
  * - the failure bitmask reported by the ssl certificate validator
- * (@c SVN_AUTH_PARAM_SSL_SERVER_FAILURES_IN)
+ * (@c SVN_AUTH_PARAM_SSL_SERVER_FAILURES)
+ *
+ * - the certificate info (svn_auth_ssl_server_cert_info_t*)
+ * (@c SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO)
  */
 void svn_client_get_ssl_server_prompt_provider
    (svn_auth_provider_object_t **provider,
Index: subversion/include/svn_auth.h
===================================================================
--- subversion/include/svn_auth.h (revision 7051)
+++ subversion/include/svn_auth.h (working copy)
@@ -190,31 +190,29 @@
 
 /** SSL server verification.
  *
- * @a cert contains two inner structures representing fields from the
- * server and issuer certificates, as well as the start and expiry
- * times. @a failures_allow is set for each flag allowed.
- *
- * failures flags defined within neon:
- * * SVN_AUTH_SSL_NOTYETVALID : certificate is not yet valid
- * * SVN_AUTH_SSL_EXPIRED : certificate has expired
- * * SVN_AUTH_SSL_CNMISMATCH : name on certificate does not match server
- * * SVN_AUTH_SSL_UNKNOWNCA : cert is signed by an untrusted authority
+ * If @a trust_permanantly is set to true by the provider, the
+ * certificate will be trusted permanently.
  */
-#define SVN_AUTH_SSL_NOTYETVALID (1<<0)
-#define SVN_AUTH_SSL_EXPIRED (1<<1)
-#define SVN_AUTH_SSL_CNMISMATCH (1<<2)
-#define SVN_AUTH_SSL_UNKNOWNCA (1<<3)
-#define SVN_AUTH_SSL_FAILMASK (0x0f)
-
 #define SVN_AUTH_CRED_SERVER_SSL "svn.ssl.server"
-
 typedef struct
 {
- int failures_allow;
-
+ svn_boolean_t trust_permanantly;
 } svn_auth_cred_server_ssl_t;
 
 
+/** SSL server certificate information.
+ */
+typedef struct {
+ const char *hostname;
+ const char *fingerprint;
+ const char *valid_from;
+ const char *valid_until;
+ const char *issuer_dname;
+
+ /* The full certificate as base-64 encoded DER */
+ const char *ascii_cert;
+} svn_auth_ssl_server_cert_info_t;
+
 
 /** Credential-constructing prompt functions. **/
 
@@ -265,23 +263,26 @@
 /** Set @a *cred by prompting the user, allocating @a *cred in @a pool.
  * @a baton is an implementation-specific closure.
  *
- * @a failures_in is a failure bitmask, see (for example)
+ * @a failures is a failure bitmask, see (for example)
  *
  * @c SVN_AUTH_SSL_NOTYETVALID
  * @c SVN_AUTH_SSL_EXPIRED
  * @c SVN_AUTH_SSL_CNMISMATCH
  * @c SVN_AUTH_SSL_UNKNOWNCA
- * @c SVN_AUTH_SSL_FAILMASK
  *
  * for more information.
  */
-typedef svn_error_t *
-(*svn_auth_ssl_server_prompt_func_t) (svn_auth_cred_server_ssl_t **cred,
- void *baton,
- int failures_in,
- apr_pool_t *pool);
+#define SVN_AUTH_SSL_NOTYETVALID (1<<0)
+#define SVN_AUTH_SSL_EXPIRED (1<<1)
+#define SVN_AUTH_SSL_CNMISMATCH (1<<2)
+#define SVN_AUTH_SSL_UNKNOWNCA (1<<3)
+typedef svn_error_t *(*svn_auth_ssl_server_prompt_func_t) (
+ svn_auth_cred_server_ssl_t **cred,
+ void *baton,
+ int failures,
+ const svn_auth_ssl_server_cert_info_t *cert_info,
+ apr_pool_t *pool);
 
-
 /** Set @a *cred by prompting the user, allocating @a *cred in @a pool.
  * @a baton is an implementation-specific closure.
  */
@@ -361,9 +362,14 @@
 
 /** The following property is for ssl server cert providers. This
     provides the detected failures by the certificate validator */
-#define SVN_AUTH_PARAM_SSL_SERVER_FAILURES_IN SVN_AUTH_PARAM_PREFIX \
+#define SVN_AUTH_PARAM_SSL_SERVER_FAILURES SVN_AUTH_PARAM_PREFIX \
   "ssl:failures"
 
+/** The following property is for ssl server cert providers. This
+ provides the cert info (svn_auth_ssl_server_cert_info_t). */
+#define SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO SVN_AUTH_PARAM_PREFIX \
+ "ssl:cert-info"
+
 /** Some providers need access to the @c svn_config_t configuration
     for individual servers in order to properly operate */
 #define SVN_AUTH_PARAM_CONFIG SVN_AUTH_PARAM_PREFIX "config"
@@ -372,6 +378,7 @@
 /** A configuration directory that overrides the default
     ~/.subversion. */
 #define SVN_AUTH_PARAM_CONFIG_DIR SVN_AUTH_PARAM_PREFIX "config-dir"
+
 /** Get an initial set of credentials.
  *
  * Ask @a auth_baton to set @a *credentials to a set of credentials
Index: subversion/libsvn_subr/config_file.c
===================================================================
--- subversion/libsvn_subr/config_file.c (revision 7051)
+++ subversion/libsvn_subr/config_file.c (working copy)
@@ -496,6 +496,12 @@
   if (kind == svn_node_none)
     apr_err = apr_dir_make (auth_subdir, APR_OS_DEFAULT, pool);
 
+ auth_subdir = svn_path_join_many (pool, auth_dir,
+ SVN_AUTH_CRED_SERVER_SSL, NULL);
+ svn_io_check_path (auth_subdir, &kind, pool);
+ if (kind == svn_node_none)
+ apr_err = apr_dir_make (auth_subdir, APR_OS_DEFAULT, pool);
+
 }
 
 
@@ -795,21 +801,20 @@
         "### including HTTP proxy information, and HTTP timeout settings.\n"
         "###\n"
         "### The currently defined server options are:\n"
- "### http-proxy-host Proxy host for HTTP connection\n"
- "### http-proxy-port Port number of proxy host service\n"
- "### http-proxy-username Username for auth to proxy service\n"
- "### http-proxy-password Password for auth to proxy service\n"
- "### http-proxy-exceptions List of sites that do not use proxy\n"
- "### http-timeout Timeout for HTTP requests in seconds\n"
- "### http-compression Whether to compress HTTP requests\n"
- "### neon-debug-mask Debug mask for Neon HTTP library\n"
- "### ssl-authority-files List of files, each of a trusted CAs\n"
- "### ssl-trust-default-ca Trust the system 'default' CAs\n"
- "### ssl-ignore-unknown-ca Allow untrusted server certificates\n"
- "### ssl-ignore-invalid-date Allow expired/postdated certificates\n"
- "### ssl-ignore-host-mismatch Allow certificates for other servers\n"
- "### ssl-client-cert-file PKCS#12 format client certificate file\n"
- "### ssl-client-cert-password Client Key password, if needed.\n"
+ "### http-proxy-host Proxy host for HTTP connection\n"
+ "### http-proxy-port Port number of proxy host service\n"
+ "### http-proxy-username Username for auth to proxy service\n"
+ "### http-proxy-password Password for auth to proxy service\n"
+ "### http-proxy-exceptions List of sites that do not use proxy\n"
+ "### http-timeout Timeout for HTTP requests in seconds\n"
+ "### http-compression Whether to compress HTTP requests\n"
+ "### neon-debug-mask Debug mask for Neon HTTP library\n"
+ "### ssl-authority-files List of files, each of a trusted CAs\n"
+ "### ssl-trust-default-ca Trust the system 'default' CAs\n"
+ "### ssl-ignore-invalid-date Allow expired/postdated certificates\n"
+ "### ssl-override-cert-hostname Override the certificate hostname\n"
+ "### ssl-client-cert-file PKCS#12 format client certificate file\n"
+ "### ssl-client-cert-password Client Key password, if needed.\n"
         "###\n"
         "### HTTP timeouts, if given, are specified in seconds. A timeout\n"
         "### of 0, i.e. zero, causes a builtin default to be used.\n"
@@ -823,10 +828,6 @@
         "### match is found, the server info is from the section with the\n"
         "### corresponding name.\n"
         "\n"
- "### Note that the ssl-ignore overrides significantly decrease the\n"
- "### security of the connection, and may allow a third party to\n"
- "### intercept or even modify the transmitted data.\n"
- "\n"
         "# [groups]\n"
         "# group1 = *.collab.net\n"
         "# othergroup = repository.blarggitywhoomph.com\n"
@@ -840,9 +841,8 @@
         "# http-proxy-password = doubleblah\n"
         "# http-timeout = 60\n"
         "# neon-debug-mask = 130\n"
- "# ssl-ignore-unknown-ca = true\n"
- "# ssl-ignore-host-mismatch = true\n"
         "# ssl-ignore-invalid-date = true\n"
+ "# ssl-override-cert-hostname = snakeoil.com\n"
         "\n"
         "### Information for the second group:\n"
         "# [othergroup]\n"
Index: subversion/libsvn_client/auth.c
===================================================================
--- subversion/libsvn_client/auth.c (revision 7051)
+++ subversion/libsvn_client/auth.c (working copy)
@@ -436,11 +436,18 @@
 
 
 /*** SSL file providers. ***/
+typedef struct {
+ /* the cred_kind being fetched (see svn_auth.h)*/
+ const char *cred_kind;
 
+ /* cache: realmstring which identifies the credentials file */
+ const char *realmstring;
+} ssl_server_file_provider_baton_t;
+
 /* retieve ssl server CA failure overrides (if any) from servers
    config */
 static svn_error_t *
-server_ssl_file_first_credentials (void **credentials_p,
+server_ssl_file_first_credentials (void **credentials,
                                    void **iter_baton,
                                    void *provider_baton,
                                    apr_hash_t *parameters,
@@ -448,58 +455,113 @@
                                    apr_pool_t *pool)
 {
   const char *temp_setting;
- int failures_in = (int) apr_hash_get (parameters,
- SVN_AUTH_PARAM_SSL_SERVER_FAILURES_IN,
- APR_HASH_KEY_STRING);
+ ssl_server_file_provider_baton_t *pb = provider_baton;
+ int failures = (int) apr_hash_get (parameters,
+ SVN_AUTH_PARAM_SSL_SERVER_FAILURES,
+ APR_HASH_KEY_STRING);
+ const svn_auth_ssl_server_cert_info_t *cert_info =
+ apr_hash_get (parameters,
+ SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO,
+ APR_HASH_KEY_STRING);
   svn_config_t *cfg = apr_hash_get (parameters,
                                     SVN_AUTH_PARAM_CONFIG,
                                     APR_HASH_KEY_STRING);
   const char *server_group = apr_hash_get (parameters,
                                            SVN_AUTH_PARAM_SERVER_GROUP,
                                            APR_HASH_KEY_STRING);
- svn_auth_cred_server_ssl_t *cred;
- int failures_allow = 0;
+ apr_hash_t *creds_hash = NULL;
+ const char *config_dir;
+ svn_error_t *error = SVN_NO_ERROR;
 
- temp_setting = svn_config_get_server_setting (cfg, server_group,
- SVN_CONFIG_OPTION_SSL_IGNORE_UNKNOWN_CA,
- "false");
- if (strcasecmp (temp_setting, "true") == 0)
+ *credentials = NULL;
+ *iter_baton = NULL;
+
+ /* Check for ignored cert dates */
+ if (failures & (SVN_AUTH_SSL_NOTYETVALID | SVN_AUTH_SSL_EXPIRED))
     {
- failures_allow |= SVN_AUTH_SSL_UNKNOWNCA;
+ temp_setting = svn_config_get_server_setting
+ (cfg, server_group,
+ SVN_CONFIG_OPTION_SSL_IGNORE_INVALID_DATE,
+ "false");
+ if (strcasecmp (temp_setting, "true") == 0)
+ {
+ failures &= ~(SVN_AUTH_SSL_NOTYETVALID | SVN_AUTH_SSL_EXPIRED);
+ }
     }
 
- temp_setting = svn_config_get_server_setting (cfg, server_group,
- SVN_CONFIG_OPTION_SSL_IGNORE_HOST_MISMATCH,
- "false");
- if (strcasecmp (temp_setting, "true") == 0)
+ /* Check for overridden cert hostname */
+ if (failures & SVN_AUTH_SSL_CNMISMATCH)
     {
- failures_allow |= SVN_AUTH_SSL_CNMISMATCH;
+ temp_setting = svn_config_get_server_setting
+ (cfg, server_group,
+ SVN_CONFIG_OPTION_SSL_OVERRIDE_CERT_HSTNAME,
+ NULL);
+ if (temp_setting && strcasecmp (temp_setting, cert_info->hostname) == 0)
+ {
+ failures &= ~SVN_AUTH_SSL_CNMISMATCH;
+ }
     }
+
 
- temp_setting = svn_config_get_server_setting (cfg, server_group,
- SVN_CONFIG_OPTION_SSL_IGNORE_INVALID_DATE,
- "false");
- if (strcasecmp (temp_setting, "true") == 0)
+ /* Check if this is a permanently accepted cert */
+ if (failures & SVN_AUTH_SSL_UNKNOWNCA)
     {
- failures_allow |= SVN_AUTH_SSL_NOTYETVALID | SVN_AUTH_SSL_EXPIRED;
+ pb->realmstring = apr_pstrdup (pool, realmstring);
+ config_dir = apr_hash_get (parameters,
+ SVN_AUTH_PARAM_CONFIG_DIR,
+ APR_HASH_KEY_STRING);
+ error = svn_config_read_auth_data (&creds_hash, pb->cred_kind,
+ pb->realmstring, config_dir, pool);
+ svn_error_clear(error);
+ if (!error && creds_hash)
+ {
+ failures &= ~SVN_AUTH_SSL_UNKNOWNCA;
+ }
     }
 
- /* don't return creds unless we consider the certificate completely
- * acceptable */
- if ( (failures_in & ~failures_allow) == 0)
+ /* Update the set of failures */
+ apr_hash_set (parameters,
+ SVN_AUTH_PARAM_SSL_SERVER_FAILURES,
+ APR_HASH_KEY_STRING,
+ (void*)failures);
+
+ /* If all failures are cleared now, we return the creds */
+ if (!failures)
     {
- cred = apr_palloc (pool, sizeof (*cred));
- *credentials_p = cred;
- cred->failures_allow = failures_allow;
+ svn_auth_cred_server_ssl_t *creds = apr_pcalloc (pool, sizeof(*creds));
+ creds->trust_permanantly = FALSE; /* No need to save it again... */
+ *credentials = creds;
     }
- else
- {
- *credentials_p = NULL;
- }
- *iter_baton = NULL;
+
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+server_ssl_file_save_credentials (svn_boolean_t *saved,
+ void *credentials,
+ void *provider_baton,
+ apr_hash_t *parameters,
+ apr_pool_t *pool)
+{
+ ssl_server_file_provider_baton_t *pb = provider_baton;
+ const char *ascii_cert = pb->realmstring;
+ apr_hash_t *creds_hash = NULL;
+ const char *config_dir;
+
+ config_dir = apr_hash_get (parameters,
+ SVN_AUTH_PARAM_CONFIG_DIR,
+ APR_HASH_KEY_STRING);
+
+ creds_hash = apr_hash_make (pool);
+ SVN_ERR (svn_config_write_auth_data (creds_hash,
+ pb->cred_kind,
+ ascii_cert,
+ config_dir,
+ pool));
+ *saved = TRUE;
+ return SVN_NO_ERROR;
+}
+
 /* retrieve and load the ssl client certificate file from servers
    config */
 static svn_error_t *
@@ -576,7 +638,7 @@
     SVN_AUTH_CRED_SERVER_SSL,
     &server_ssl_file_first_credentials,
     NULL,
- NULL
+ &server_ssl_file_save_credentials,
   };
 
 static const svn_auth_provider_t client_ssl_cert_file_provider =
@@ -604,7 +666,12 @@
                                          apr_pool_t *pool)
 {
   svn_auth_provider_object_t *po = apr_pcalloc (pool, sizeof(*po));
+ ssl_server_file_provider_baton_t *pb = apr_pcalloc (pool, sizeof(*pb));
+
+ pb->cred_kind = SVN_AUTH_CRED_SERVER_SSL;
+
   po->vtable = &server_ssl_file_provider;
+ po->provider_baton = pb;
   *provider = po;
 }
 
@@ -701,13 +768,23 @@
                               apr_pool_t *pool)
 {
   cred_server_ssl_provider_baton_t *pb = provider_baton;
- int failures_in = (int) apr_hash_get (parameters,
- SVN_AUTH_PARAM_SSL_SERVER_FAILURES_IN,
- APR_HASH_KEY_STRING);
+ int failures = (int) apr_hash_get (parameters,
+ SVN_AUTH_PARAM_SSL_SERVER_FAILURES,
+ APR_HASH_KEY_STRING);
+ const svn_auth_ssl_server_cert_info_t *cert_info =
+ apr_hash_get (parameters,
+ SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO,
+ APR_HASH_KEY_STRING);
 
   SVN_ERR (pb->prompt_func ((svn_auth_cred_server_ssl_t **) credentials_p,
- pb->prompt_baton, failures_in, pool));
+ pb->prompt_baton, failures, cert_info, pool));
 
+ /* Store the potentially updated failures mask in the hash */
+ apr_hash_set (parameters,
+ SVN_AUTH_PARAM_SSL_SERVER_FAILURES,
+ APR_HASH_KEY_STRING,
+ (void*)failures);
+
   *iter_baton = NULL;
   return SVN_NO_ERROR;
 }
Index: subversion/clients/cmdline/cl.h
===================================================================
--- subversion/clients/cmdline/cl.h (revision 7051)
+++ subversion/clients/cmdline/cl.h (working copy)
@@ -275,10 +275,12 @@
 
 /* This implements 'svn_auth_ssl_server_prompt_func_t'. */
 svn_error_t *
-svn_cl__auth_ssl_server_prompt (svn_auth_cred_server_ssl_t **cred_p,
- void *baton,
- int failures_in,
- apr_pool_t *pool);
+svn_cl__auth_ssl_server_prompt (
+ svn_auth_cred_server_ssl_t **cred_p,
+ void *baton,
+ int failures,
+ const svn_auth_ssl_server_cert_info_t *cert_info,
+ apr_pool_t *pool);
 
 
 
Index: subversion/clients/cmdline/prompt.c
===================================================================
--- subversion/clients/cmdline/prompt.c (revision 7051)
+++ subversion/clients/cmdline/prompt.c (working copy)
@@ -182,54 +182,75 @@
 
 /* This implements 'svn_auth_ssl_server_prompt_func_t'. */
 svn_error_t *
-svn_cl__auth_ssl_server_prompt (svn_auth_cred_server_ssl_t **cred_p,
- void *baton,
- int failures_in,
- apr_pool_t *pool)
+svn_cl__auth_ssl_server_prompt (
+ svn_auth_cred_server_ssl_t **cred_p,
+ void *baton,
+ int failures,
+ const svn_auth_ssl_server_cert_info_t *cert_info,
+ apr_pool_t *pool)
 {
- svn_boolean_t previous_output = FALSE;
- int failure;
+ int allow_perm_accept = failures & SVN_AUTH_SSL_UNKNOWNCA;
   const char *choice;
-
   svn_stringbuf_t *buf = svn_stringbuf_create
- ("Error validating server certificate: ", pool);
+ ("Error validating server certificate:\n", pool);
 
- failure = failures_in & SVN_AUTH_SSL_UNKNOWNCA;
- if (failure)
+ if (failures & SVN_AUTH_SSL_UNKNOWNCA)
     {
- svn_stringbuf_appendcstr (buf, "Unknown certificate issuer");
- previous_output = TRUE;
+ svn_stringbuf_appendcstr (buf, " - Unknown certificate issuer\n");
+ svn_stringbuf_appendcstr (buf, " Fingerprint: ");
+ svn_stringbuf_appendcstr (buf, cert_info->fingerprint);
+ svn_stringbuf_appendcstr (buf, "\n");
+ svn_stringbuf_appendcstr (buf, " Distinguished name: ");
+ svn_stringbuf_appendcstr (buf, cert_info->issuer_dname);
+ svn_stringbuf_appendcstr (buf, "\n");
     }
 
- failure = failures_in & SVN_AUTH_SSL_CNMISMATCH;
- if (failure)
+ if (failures & SVN_AUTH_SSL_CNMISMATCH)
     {
- if (previous_output)
- {
- svn_stringbuf_appendcstr (buf, ", ");
- }
- svn_stringbuf_appendcstr (buf, "Hostname mismatch");
- previous_output = TRUE;
+ svn_stringbuf_appendcstr (buf, " - Hostname mismatch (");
+ svn_stringbuf_appendcstr (buf, cert_info->hostname);
+ svn_stringbuf_appendcstr (buf, ")\n");
     }
- failure = failures_in & (SVN_AUTH_SSL_EXPIRED | SVN_AUTH_SSL_NOTYETVALID);
- if (failure)
+
+ if (failures & SVN_AUTH_SSL_NOTYETVALID)
     {
- if (previous_output)
- {
- svn_stringbuf_appendcstr (buf, ", ");
- }
- svn_stringbuf_appendcstr (buf, "Certificate expired or not yet valid");
- previous_output = TRUE;
+ svn_stringbuf_appendcstr (buf, " - Certificate is not yet valid\n");
+ svn_stringbuf_appendcstr (buf, " Valid from ");
+ svn_stringbuf_appendcstr (buf, cert_info->valid_from);
+ svn_stringbuf_appendcstr (buf, "\n");
     }
 
- svn_stringbuf_appendcstr (buf, ". Accept? (y/N): ");
+ if (failures & SVN_AUTH_SSL_EXPIRED)
+ {
+ svn_stringbuf_appendcstr (buf, " - Certificate has expired\n");
+ svn_stringbuf_appendcstr (buf, " Valid until ");
+ svn_stringbuf_appendcstr (buf, cert_info->valid_until);
+ svn_stringbuf_appendcstr (buf, "\n");
+ }
+
+ if (allow_perm_accept)
+ {
+ svn_stringbuf_appendcstr (buf,
+ "(R)eject, accept (t)emporarily or accept "
+ "(p)ermanently? ");
+ }
+ else
+ {
+ svn_stringbuf_appendcstr (buf, "(R)eject or accept (t)emporarily? ");
+ }
   SVN_ERR (prompt (&choice, buf->data, FALSE, pool));
-
- if (choice && (choice[0] == 'y' || choice[0] == 'Y'))
+
+ if (choice && (choice[0] == 't' || choice[0] == 'T'))
     {
       *cred_p = apr_pcalloc (pool, sizeof (**cred_p));
- (*cred_p)->failures_allow = failures_in;
+ (*cred_p)->trust_permanantly = FALSE;
     }
+ else if (allow_perm_accept &&
+ choice && (choice[0] == 'p' || choice[0] == 'P'))
+ {
+ *cred_p = apr_pcalloc (pool, sizeof (**cred_p));
+ (*cred_p)->trust_permanantly = TRUE;
+ }
   else
     {
       *cred_p = NULL;
Index: subversion/libsvn_ra_dav/session.c
===================================================================
--- subversion/libsvn_ra_dav/session.c (revision 7051)
+++ subversion/libsvn_ra_dav/session.c (working copy)
@@ -117,23 +117,47 @@
                     const ne_ssl_certificate *cert)
 {
   svn_ra_session_t *ras = userdata;
+ svn_auth_cred_server_ssl_t *server_creds = NULL;
   void *creds;
- svn_auth_cred_server_ssl_t *server_creds;
   svn_auth_iterstate_t *state;
   apr_pool_t *pool;
   svn_error_t *error;
- int failures_allowed = 0;
+ char *ascii_cert = ne_ssl_cert_export(cert);
+ char *issuer_dname = ne_ssl_readable_dname(ne_ssl_cert_issuer(cert));;
+ svn_auth_ssl_server_cert_info_t cert_info;
+ char fingerprint[NE_SSL_DIGESTLEN];
+ char valid_from[NE_SSL_VDATELEN], valid_until[NE_SSL_VDATELEN];
 
   svn_auth_set_parameter(ras->callbacks->auth_baton,
- SVN_AUTH_PARAM_SSL_SERVER_FAILURES_IN,
+ SVN_AUTH_PARAM_SSL_SERVER_FAILURES,
                          (void*)failures);
 
+ /* Extract the info from the certificate */
+ cert_info.hostname = ne_ssl_cert_identity(cert);
+ if (ne_ssl_cert_digest(cert, fingerprint) != 0)
+ {
+ strcpy(fingerprint, "<unknown>");
+ }
+ cert_info.fingerprint = fingerprint;
+ ne_ssl_cert_validity(cert, valid_from, valid_until);
+ cert_info.valid_from = valid_from;
+ cert_info.valid_until = valid_until;
+ cert_info.issuer_dname = issuer_dname;
+ cert_info.ascii_cert = ascii_cert;
+
+ svn_auth_set_parameter(ras->callbacks->auth_baton,
+ SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO,
+ &cert_info);
+
   apr_pool_create(&pool, ras->pool);
   error = svn_auth_first_credentials(&creds, &state,
                                      SVN_AUTH_CRED_SERVER_SSL,
- "none", /* ### fix? */
+ ascii_cert,
                                      ras->callbacks->auth_baton,
                                      pool);
+ free(issuer_dname);
+ free(ascii_cert);
+
   if (error || !creds)
     {
       svn_error_clear(error);
@@ -141,10 +165,19 @@
   else
     {
       server_creds = creds;
- failures_allowed = (server_creds) ? server_creds->failures_allow : 0;
+ if (server_creds->trust_permanantly)
+ {
+ error = svn_auth_save_credentials(state, pool);
+ if (error)
+ {
+ /* It would be nice to show the error to the user
+ * somehow... */
+ svn_error_clear(error);
+ }
+ }
     }
   apr_pool_destroy(pool);
- return (failures & ~failures_allowed);
+ return !server_creds;
 }
 
 static int

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Fri Sep 12 02:56:58 2003

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.