Index: configure.in
===================================================================
--- configure.in	(revision 20716)
+++ configure.in	(working copy)
@@ -256,6 +256,11 @@
 
 SVN_LIB_SASL
 
+if test "$svn_lib_sasl" = "yes"; then
+  AC_DEFINE(SVN_HAVE_SASL, 1,
+            [Defined if Cyrus SASL v2 is present on the system])
+fi
+
 dnl Mac OS KeyChain -------------------
 
 AC_ARG_ENABLE(keychain,
Index: subversion/libsvn_ra_svn/client.c
===================================================================
--- subversion/libsvn_ra_svn/client.c	(revision 20716)
+++ subversion/libsvn_ra_svn/client.c	(working copy)
@@ -1929,6 +1929,10 @@
 
   *vtable = &ra_svn_vtable;
 
+#ifdef SVN_HAVE_SASL
+  SVN_ERR(svn_ra_svn__sasl_init());
+#endif
+
   return SVN_NO_ERROR;
 }
 
Index: subversion/libsvn_ra_svn/ra_svn_sasl.h
===================================================================
--- subversion/libsvn_ra_svn/ra_svn_sasl.h	(revision 0)
+++ subversion/libsvn_ra_svn/ra_svn_sasl.h	(revision 0)
@@ -0,0 +1,48 @@
+/*
+ * ra_svn_sasl.h :  SASL-related declarations shared between the 
+ * ra_svn and svnserve module
+ *
+ * ====================================================================
+ * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution.  The terms
+ * are also available at http://subversion.tigris.org/license-1.html.
+ * If newer versions of this license are posted there, you may use a
+ * newer version instead, at your option.
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals.  For exact contribution history, see the revision
+ * history and logs, available at http://subversion.tigris.org/.
+ * ====================================================================
+ */
+
+
+
+#ifndef RA_SVN_SASL_H
+#define RA_SVN_SASL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <sasl/sasl.h>
+
+#define DEFAULT_SECPROPS {0, 256, 4096, 0, NULL, NULL}
+
+/* This function is called by the client and the server before
+ * calling sasl_{client, server}_init */
+apr_status_t svn_ra_svn__sasl_common_init();
+
+/* Sets local_addrport and remote_addrport 
+ * to a string containing the remote and local IP address and port,
+ * formatted like this: a.b.c.d;port */
+void svn_ra_svn__get_addresses(apr_socket_t *sock,
+                               char **local_addrport, 
+                               char **remote_addrport,
+                               apr_pool_t *pool);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif  /* RA_SVN_SASL_H */
Index: subversion/libsvn_ra_svn/sasl_auth.c
===================================================================
--- subversion/libsvn_ra_svn/sasl_auth.c	(revision 0)
+++ subversion/libsvn_ra_svn/sasl_auth.c	(revision 0)
@@ -0,0 +1,540 @@
+/*
+ * sasl_auth.c :  Functions for SASL-based authentication
+ *
+ * ====================================================================
+ * Copyright (c) 2000-2006 CollabNet.  All rights reserved.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution.  The terms
+ * are also available at http://subversion.tigris.org/license-1.html.
+ * If newer versions of this license are posted there, you may use a
+ * newer version instead, at your option.
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals.  For exact contribution history, see the revision
+ * history and logs, available at http://subversion.tigris.org/.
+ * ====================================================================
+ */
+
+#define APR_WANT_STRFUNC
+#include <apr_want.h>
+#include <apr_general.h>
+#include <apr_strings.h>
+#include <apr_atomic.h>
+#include <apr_thread_mutex.h>
+#include <apr_version.h>
+
+#include "svn_types.h"
+#include "svn_string.h"
+#include "svn_error.h"
+#include "svn_pools.h"
+#include "svn_private_config.h"
+#include "svn_ra.h"
+#include "svn_ra_svn.h"
+#include "svn_base64.h"
+
+#include "ra_svn.h"
+#include "ra_svn_sasl.h"
+
+#ifdef SVN_HAVE_SASL
+
+#ifdef APR_HAS_THREADS
+
+/* Cyrus SASL is thread-safe only if we supply it with mutex functions
+ * (with sasl_set_mutex()).  To make this work with APR, we need to use a
+ * global pool for the mutex allocations.  Freeing a mutex actually
+ * returns it to a global array.  We allocate mutexes from this
+ * array if it is non-empty, or directly from the pool otherwise.
+ * We also need a mutex to serialize accesses to the array itself.
+ */
+
+/* We allocate mutexes for SASL from this pool. */
+static apr_pool_t *sasl_pool = NULL;
+
+/* An array of allocated, but unused, mutexes. */
+static apr_array_header_t *free_mutexes = NULL;
+
+/* A mutex to serialize access to the array. */
+static apr_thread_mutex_t *array_mutex;
+
+/* Callbacks we pass to sasl_set_mutex. */
+
+static void *sasl_mutex_alloc_cb(void)
+{
+  apr_thread_mutex_t *mutex;
+  if (apr_is_empty_array(free_mutexes))
+    {
+      int r = apr_thread_mutex_create(&mutex,
+                                      APR_THREAD_MUTEX_DEFAULT, 
+                                      sasl_pool);
+      if (r != APR_SUCCESS)
+        return NULL;
+    }
+  else
+    {
+      apr_thread_mutex_lock(array_mutex);
+      mutex =  *((apr_thread_mutex_t**)apr_array_pop(free_mutexes));
+      apr_thread_mutex_unlock(array_mutex);
+    }
+  return mutex;
+}
+
+static int sasl_mutex_lock_cb(void *mutex)
+{
+  if (mutex)
+    return (apr_thread_mutex_lock(mutex) == APR_SUCCESS) ? 0 : -1;
+  else
+    return 0;
+}
+
+static int sasl_mutex_unlock_cb(void *mutex)
+{
+  if (mutex)
+    return (apr_thread_mutex_unlock(mutex) == APR_SUCCESS) ? 0 : -1;
+  else
+    return 0;
+}
+
+static void sasl_mutex_free_cb(void *mutex)
+{
+  apr_thread_mutex_lock(array_mutex);
+  *((apr_thread_mutex_t**)apr_array_push(free_mutexes)) = mutex;
+  apr_thread_mutex_unlock(array_mutex);
+}
+#endif /* APR_HAS_THREADS */
+
+static sasl_callback_t interactions[] =
+{
+  /* Use SASL interactions for username & password */
+  {SASL_CB_AUTHNAME, NULL, NULL}, 
+  {SASL_CB_PASS, NULL, NULL},
+  {SASL_CB_LIST_END, NULL, NULL}
+};
+
+apr_status_t svn_ra_svn__sasl_common_init()
+{
+  apr_status_t apr_err = APR_SUCCESS;
+
+  atexit(sasl_done);
+#ifdef APR_HAS_THREADS
+  sasl_set_mutex(sasl_mutex_alloc_cb,
+                 sasl_mutex_lock_cb,
+                 sasl_mutex_unlock_cb,
+                 sasl_mutex_free_cb);
+  sasl_pool = svn_pool_create(NULL);
+  free_mutexes = apr_array_make(sasl_pool, 0, sizeof(apr_thread_mutex_t *));
+  apr_err = apr_thread_mutex_create(&array_mutex,
+                                    APR_THREAD_MUTEX_DEFAULT, 
+                                    sasl_pool);
+#endif /* APR_HAS_THREADS */
+  return apr_err;
+}
+
+/* ### These defines were taken from libsvn_fs_base/bdb/env.c.
+ * Perhaps they should be moved to svn_private_config.h as suggested there? */
+#if APR_MAJOR_VERSION > 0
+# define svn__atomic_t apr_uint32_t
+# define svn__atomic_cas(mem, with, cmp) \
+    apr_atomic_cas32((mem), (with), (cmp))
+#else
+# define svn__atomic_t apr_atomic_t
+# define svn__atomic_cas(mem, with, cmp) \
+    apr_atomic_cas((mem), (with), (cmp))
+#endif
+
+/* Magic values for atomic initialization of the SASL library. */
+#define SASL_UNINITIALIZED 0
+#define SASL_START_INIT    1
+#define SASL_INIT_FAILED   2
+#define SASL_INITIALIZED   3
+
+static volatile svn__atomic_t sasl_status = SASL_UNINITIALIZED;
+
+svn_error_t *svn_ra_svn__sasl_init()
+{
+  /* We have to initialize the cache exactly once.  Because APR
+     doesn't have statically-initialized mutexes, we implement a poor
+     man's spinlock using svn__atomic_cas. */
+#ifdef APR_HAS_THREADS
+  svn__atomic_t status = svn__atomic_cas(&sasl_status, 
+                                         SASL_START_INIT, 
+                                         SASL_UNINITIALIZED);
+  if (status == SASL_UNINITIALIZED)
+#else
+  if (!sasl_pool)
+#endif /* APR_HAS_THREADS */
+    {
+      if (svn_ra_svn__sasl_common_init() != APR_SUCCESS
+          || sasl_client_init(interactions) != SASL_OK)
+        {
+#ifdef APR_HAS_THREADS
+          svn__atomic_cas(&sasl_status, SASL_INIT_FAILED, SASL_START_INIT);
+#endif /* APR_HAS_THREADS */
+          return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+                                  _("Could not initialize the SASL library"));
+        }
+#ifdef APR_HAS_THREADS
+      svn__atomic_cas(&sasl_status, SASL_INITIALIZED, SASL_START_INIT);
+#endif /* APR_HAS_THREADS */
+    }
+#ifdef APR_HAS_THREADS
+  /* Make sure that the other threads wait for
+     the initialization to finish */
+  else while (status != SASL_INITIALIZED)
+    {
+      if (status == SASL_INIT_FAILED)
+        return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+                                _("Could not initialize the SASL library"));
+      apr_sleep(APR_USEC_PER_SEC / 1000);
+      status = svn__atomic_cas(&sasl_status, 
+                               SASL_UNINITIALIZED, 
+                               SASL_UNINITIALIZED);
+    }
+#endif /* APR_HAS_THREADS */
+  return SVN_NO_ERROR;
+}
+
+/* Create a new SASL context. */
+static svn_error_t *new_sasl_ctx(sasl_conn_t **sasl_ctx,
+                                 svn_boolean_t is_tunneled,
+                                 const char *hostname, 
+                                 const char *local_addrport,
+                                 const char *remote_addrport, 
+                                 apr_pool_t *pool)
+{
+  sasl_security_properties_t secprops = DEFAULT_SECPROPS;
+  int result;
+
+  result = sasl_client_new("svn", hostname, local_addrport, remote_addrport,
+                           interactions, SASL_SUCCESS_DATA, 
+                           sasl_ctx);
+  if (result != SASL_OK)
+    return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+                            sasl_errstring(result, NULL, NULL));
+
+
+  if (is_tunneled)
+    {
+      /* We need to tell SASL that this connection is tunneled,
+       * otherwise it will ignore EXTERNAL. The third paramater
+       * should be the username, but since SASL doesn't seem
+       * to use it on the client side, any non-empty string will do. */
+      result = sasl_setprop(*sasl_ctx, 
+                            SASL_AUTH_EXTERNAL, " ");
+      if (result != SASL_OK)
+        return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+                                sasl_errdetail(*sasl_ctx));
+   }
+
+  /* Set security properties.
+   * Don't allow PLAIN or LOGIN, since we don't support TLS yet. */
+  secprops.security_flags = SASL_SEC_NOPLAINTEXT;
+  sasl_setprop(*sasl_ctx, SASL_SEC_PROPS, &secprops);
+
+  return SVN_NO_ERROR;
+}
+
+/* Get the username and password from the client */
+static svn_error_t* get_creds(svn_auth_iterstate_t **iterstate_p,
+                              svn_ra_svn__session_baton_t *sess,
+                              const char *realm,
+                              const char *last_err,
+                              const char **username, const char **password,
+                              apr_pool_t *pool)
+{
+  void *creds;
+
+  if (*iterstate_p == NULL)
+    {
+      /* If iterstate_p is NULL (and thus unallocated), we need to call
+         svn_auth_first_credentials(). */
+      const char *realmstring;
+
+      realmstring = realm ? 
+                    apr_psprintf(pool, "%s %s", sess->realm_prefix, realm)
+                    : sess->realm_prefix;
+      SVN_ERR(svn_auth_first_credentials(&creds, iterstate_p,
+                                         SVN_AUTH_CRED_SIMPLE, 
+                                         realmstring,
+                                         sess->auth_baton, pool));
+    }
+  else
+    SVN_ERR(svn_auth_next_credentials(&creds, *iterstate_p, pool));
+
+  if (!creds)
+    return svn_error_createf(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+                             _("Authentication error from server: %s"),
+                             last_err);
+
+  *username = ((svn_auth_cred_simple_t *)creds)->username;
+  *password = ((svn_auth_cred_simple_t *)creds)->password;
+  return SVN_NO_ERROR;
+}
+
+/* Fill in the information requested by client_interact */
+static svn_error_t *handle_interact(svn_auth_iterstate_t **iterstate_p,
+                                    svn_ra_svn__session_baton_t *sess,
+                                    sasl_interact_t *client_interact,
+                                    const char* realm,
+                                    const char *last_err,
+                                    apr_pool_t *pool)
+{
+  sasl_interact_t *prompt;
+  const char *username=NULL, *password=NULL;
+
+  SVN_ERR(get_creds(iterstate_p, sess, realm, last_err,
+                    &username, &password, pool));
+
+  for (prompt = client_interact; prompt->id != SASL_CB_LIST_END; prompt++)
+    {
+      switch (prompt->id)
+        {
+        case SASL_CB_AUTHNAME:
+          prompt->result = username;
+          prompt->len = strlen(username);
+          break;
+        case SASL_CB_PASS:
+          prompt->result = password;
+          prompt->len = strlen(password);
+          break;
+        default:
+          /* This should never be reached */
+          return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+                                  _("Unhandled SASL interaction"));
+          break;
+        }
+    }
+  return SVN_NO_ERROR;
+}
+
+/* Perform an authentication exchange */
+static svn_error_t *try_auth(svn_ra_svn__session_baton_t *sess,
+                             sasl_conn_t *sasl_ctx,
+                             svn_auth_iterstate_t **iterstate_p,
+                             svn_boolean_t *success,
+                             const char **last_err,
+                             const char *realm,
+                             const char *mechstring,
+                             svn_boolean_t compat,
+                             apr_pool_t *pool)
+{
+  sasl_interact_t *client_interact = NULL;
+  const char *out, *mech, *status = NULL;
+  const svn_string_t *arg = NULL, *in;
+  int result;
+  unsigned int outlen;
+
+  do
+    {
+      result = sasl_client_start(sasl_ctx,
+                                 mechstring,
+                                 &client_interact,
+                                 &out,
+                                 &outlen,
+                                 &mech);
+
+      /* Fill in username and password, if required */
+      if (result == SASL_INTERACT)
+        SVN_ERR(handle_interact(iterstate_p, sess, client_interact,
+                                realm, *last_err, pool));
+    }
+  while (result == SASL_INTERACT);
+
+  if (result != SASL_OK && result != SASL_CONTINUE)
+    return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+                            sasl_errdetail(sasl_ctx));
+
+  /* Prepare the initial authentication token. */
+  if (outlen > 0 || strcmp(mech, "EXTERNAL") == 0) 
+    arg = svn_base64_encode_string(svn_string_ncreate(out, outlen, pool), 
+                                   pool);
+
+  /* Send the initial client response */
+  SVN_ERR(svn_ra_svn__auth_response(sess->conn, pool, mech, 
+                                    arg ? arg->data : NULL, compat));
+
+  while (result == SASL_CONTINUE) 
+    {
+      /* Read the server response */
+      SVN_ERR(svn_ra_svn_read_tuple(sess->conn, pool, "w(?s)", 
+                                    &status, &in));
+
+      if (strcmp(status, "failure") == 0)
+        {
+          /* Authentication failed.  Use the next set of credentials */
+          *success = FALSE;
+          /* Remember the message sent by the server because we'll want to
+           * return a meaningful error if we run out of auth providers. */
+          *last_err = in ? in->data : "";
+          return SVN_NO_ERROR;
+        }
+
+      if ((strcmp(status, "success") != 0 && strcmp(status, "step") != 0) 
+          || in == NULL)
+        return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+                                _("Unexpected server response"
+                                " to authentication"));
+
+      /* If the mech is CRAM-MD5 we don't base64-decode the server response. */
+      if (strcmp(mech, "CRAM-MD5") != 0) 
+        in = svn_base64_decode_string(in, pool);
+ 
+      do
+        {
+          result = sasl_client_step(sasl_ctx, 
+                                    in->data,
+                                    in->len,
+                                    &client_interact, 
+                                    &out, /* Filled in by SASL. */
+                                    &outlen);
+
+          /* Fill in username and password, if required. */
+          if (result == SASL_INTERACT)
+            SVN_ERR(handle_interact(iterstate_p, sess, client_interact, 
+                                    realm, *last_err, pool));
+        }
+      while (result == SASL_INTERACT);
+
+      if (result != SASL_OK && result != SASL_CONTINUE)
+        return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+                                sasl_errdetail(sasl_ctx));
+
+      if (outlen > 0)
+        {
+          arg = svn_string_ncreate(out, outlen, pool); 
+          /* Write our response. */
+          /* For CRAM-MD5, we don't use base64-encoding. */
+          if (strcmp(mech, "CRAM-MD5") != 0)
+            arg = svn_base64_encode_string(arg, pool);
+          SVN_ERR(svn_ra_svn_write_cstring(sess->conn, pool, arg->data));
+        }
+    }
+
+  if (!status || strcmp(status, "step") == 0)
+    {
+      /* This is a client-send-last mech.  Read the last server response. */
+      SVN_ERR(svn_ra_svn_read_tuple(sess->conn, pool, "w(?s)", 
+              &status, &in));
+
+      if (strcmp(status, "failure") == 0)
+        {
+          *success = FALSE;
+          *last_err = in ? in->data : "";
+        }
+      else if (strcmp(status, "success") == 0)
+        {
+          /* We're done */
+          *success = TRUE;
+        }
+      else
+        return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+                                _("Unexpected server response"
+                                " to authentication"));
+    }
+  else
+    *success = TRUE;
+  return SVN_NO_ERROR;
+}
+
+void svn_ra_svn__get_addresses(apr_socket_t *sock,
+                               char **local_addrport, 
+                               char **remote_addrport,
+                               apr_pool_t *pool)
+{
+  apr_sockaddr_t *local_sa, *remote_sa;
+  char *local_addr, *remote_addr;
+ 
+  apr_socket_addr_get(&local_sa, APR_LOCAL, sock);
+  apr_socket_addr_get(&remote_sa, APR_REMOTE, sock);
+
+  apr_sockaddr_ip_get(&local_addr, local_sa);
+  apr_sockaddr_ip_get(&remote_addr, remote_sa);
+
+  /* Format the IP address and port number like this: a.b.c.d;port */
+  *local_addrport = apr_pstrcat(pool, local_addr, ";",
+                                apr_itoa(pool, (int)local_sa->port), NULL);
+  *remote_addrport = apr_pstrcat(pool, remote_addr, ";", 
+                                 apr_itoa(pool, (int)remote_sa->port), NULL);
+}
+
+void get_remote_hostname(apr_socket_t *sock, char **hostname)
+{
+  apr_sockaddr_t *sa;
+  apr_socket_addr_get(&sa, APR_REMOTE, sock);
+  apr_getnameinfo(hostname, sa, 0);
+}
+
+svn_error_t *svn_ra_svn__do_auth(svn_ra_svn__session_baton_t *sess,
+                                     apr_array_header_t *mechlist,
+                                     const char *realm, apr_pool_t *pool)
+{
+  apr_pool_t *subpool;
+  sasl_conn_t *sasl_ctx;
+  const char *mechstring = "", *last_err = "";
+  svn_ra_svn_item_t *elt;
+  svn_auth_iterstate_t *iterstate = NULL;
+  svn_boolean_t success, compat = (realm == NULL);
+  char *hostname = NULL, *local_addrport = NULL, *remote_addrport = NULL;
+  int i;
+
+  if (!sess->is_tunneled)
+    {
+      svn_ra_svn__get_addresses(sess->conn->sock, 
+                                &local_addrport, &remote_addrport, 
+                                pool);
+      get_remote_hostname(sess->conn->sock, &hostname);
+    }
+
+  subpool = svn_pool_create(pool);
+
+  /* Create a string containing the list of mechanisms, separated by spaces. */
+  for (i = 0; i < mechlist->nelts; i++)
+    {
+      elt = &APR_ARRAY_IDX(mechlist, i, svn_ra_svn_item_t);
+
+      /* Force the client to use ANONYMOUS if it is available. */
+      if (strcmp(elt->u.word, "ANONYMOUS") == 0)
+        {
+          mechstring = "ANONYMOUS";
+          break;
+        }
+
+      mechstring = apr_pstrcat(subpool, 
+                               mechstring, 
+                               i == 0 ? "" : " ", 
+                               elt->u.word, NULL);
+    }
+
+  do
+    {
+      /* We shouldn't have to create a new SASL context for each
+       * authentication attempt, but it seems that sasl_client_start
+       * forgets to initialise some data structures.  This means that
+       * the library will treat calls to sasl_client_step as if they were part
+       * of the previous auth exchange, which will obviously fail. */
+      SVN_ERR(new_sasl_ctx(&sasl_ctx, sess->is_tunneled,
+                           hostname, local_addrport, remote_addrport,
+                           pool));
+
+      SVN_ERR(try_auth(sess,
+                       sasl_ctx,
+                       &iterstate,
+                       &success,
+                       &last_err,
+                       realm,
+                       mechstring,
+                       compat,
+                       subpool));
+
+      /* Dispose of this SASL context before creating a new one. */
+      sasl_dispose(&sasl_ctx);
+    }
+  while (!success);
+
+  SVN_ERR(svn_auth_save_credentials(iterstate, pool));
+
+  svn_pool_destroy(subpool);
+  return SVN_NO_ERROR;
+}
+
+#endif /* SVN_HAVE_SASL */
Index: subversion/libsvn_ra_svn/simple_auth.c
===================================================================
--- subversion/libsvn_ra_svn/simple_auth.c	(revision 20716)
+++ subversion/libsvn_ra_svn/simple_auth.c	(working copy)
@@ -31,6 +31,8 @@
 
 #include "ra_svn.h"
 
+#ifndef SVN_HAVE_SASL
+
 static svn_boolean_t find_mech(apr_array_header_t *mechlist, const char *mech)
 {
   int i;
@@ -114,3 +116,5 @@
     return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
                             _("Cannot negotiate authentication mechanism"));
 }
+
+#endif /* SVN_HAVE_SASL */
Index: subversion/libsvn_ra_svn/ra_svn.h
===================================================================
--- subversion/libsvn_ra_svn/ra_svn.h	(revision 20716)
+++ subversion/libsvn_ra_svn/ra_svn.h	(working copy)
@@ -122,6 +122,10 @@
                                        const char *mech, const char *mech_arg,
                                        svn_boolean_t compat);
 
+/* Initialize the SASL library */
+svn_error_t *svn_ra_svn__sasl_init();
+
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
