Author: cmpilato
Date: Tue Apr 10 19:46:12 2012
New Revision: 1311958

URL: http://svn.apache.org/viewvc?rev=1311958&view=rev
Log:
On the 'master-passphrase' branch, introduce the concept of master
passphrase providers.  None of this code is actually used yet.

* subversion/include/private/svn_auth_private.h
  (svn_auth__masterpass_provider_t, svn_auth__masterpass_provider_object_t,
   svn_auth__masterpass_provider_func_t, svn_auth__get_masterpass_providers,
   svn_auth__get_gpg_agent_masterpass_provider): New typedefs and
    function prototypes.

* subversion/libsvn_subr/auth.c
  (AUTHN_MASTER_PASS_KNOWN_TEXT, AUTHN_FAUX_REALMSTRING, AUTHN_CHECKTEXT_KEY,
   AUTHN_PASSTYPE_KEY, encrypt_text, decrypt_text,
   svn_auth_master_passphrase_get, svn_auth_master_passphrase_set):
    Moved these to masterpass.c.

* subversion/libsvn_subr/masterpass.c
  New file, containing the stuff moved out of auth.c, plus...
  (get_masterpass_provider, svn_auth__get_masterpass_providers): ...these new
    functions.

* subversion/libsvn_subr/gpg_agent.c
  (cacheid_from_realmstring, password_get_gpg_agent_helper): New
    helper functions, mostly carved out of password_get_gpg_agent().
  (password_get_gpg_agent): Reimplement has a wrapper around
    cacheid_from_realmstring() and password_get_gpg_agent_helper().
  (gpg_agent_masterpass_fetch, gpg_agent_masterpass_store,
   gpg_agent_masterpass_provider, svn_auth__get_gpg_agent_masterpass_provider):
    New functions and static structures.

Added:
    subversion/branches/master-passphrase/subversion/libsvn_subr/masterpass.c   
(with props)
Modified:
    
subversion/branches/master-passphrase/subversion/include/private/svn_auth_private.h
    subversion/branches/master-passphrase/subversion/libsvn_subr/auth.c
    subversion/branches/master-passphrase/subversion/libsvn_subr/gpg_agent.c

Modified: 
subversion/branches/master-passphrase/subversion/include/private/svn_auth_private.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/include/private/svn_auth_private.h?rev=1311958&r1=1311957&r2=1311958&view=diff
==============================================================================
--- 
subversion/branches/master-passphrase/subversion/include/private/svn_auth_private.h
 (original)
+++ 
subversion/branches/master-passphrase/subversion/include/private/svn_auth_private.h
 Tue Apr 10 19:46:12 2012
@@ -213,6 +213,63 @@ svn_auth__ssl_client_cert_pw_set(svn_boo
                                  svn_boolean_t non_interactive,
                                  apr_pool_t *pool);
 
+
+/*** Master Passphrase ***/
+
+/** The master passphrase "provider" vtable. */
+typedef struct svn_auth__masterpass_provider_t
+{
+   /* Set *PASSPHRASE to the value of the Subversion master passphrase
+      hash digest string.  If NON_INTERACTIVE is set, do not prompt
+      the user.  Set *DONE to TRUE if the passphrase is successfully
+      fetched; to FALSE otherwise. */
+  svn_error_t *
+  (*svn_auth__masterpass_fetch_t)(const char **passphrase,
+                                  svn_boolean_t non_interactive,
+                                  void *provider_baton,
+                                  apr_pool_t *pool);
+
+   /* Store PASSPHRASE as the value of the Subversion master
+      passphrase hash digest string.  If NON_INTERACTIVE is set, do
+      not prompt the user.  Set *DONE to TRUE if the passphrase is
+      successfully stored; to FALSE otherwise. */
+  svn_error_t *
+  (*svn_auth__masterpass_store_t)(const char *passphrase,
+                                  svn_boolean_t non_interactive,
+                                  void *provider_baton,
+                                  apr_pool_t *pool);
+
+} svn_auth__masterpass_provider_t;
+
+/** A master passphrase provider object and baton. */
+typedef struct svn__auth_masterpass_provider_object_t
+{
+  const svn_auth__masterpass_provider_t *vtable;
+  void *provider_baton;
+
+} svn_auth__masterpass_provider_object_t;
+
+/** The type of function returning a master passphrase provider. */
+typedef void (*svn_auth__masterpass_provider_func_t)(
+    svn_auth__masterpass_provider_object_t **provider,
+    apr_pool_t *pool);
+
+/* Set *PROVIDERS to an array of svn_auth_provider_object_t's
+   appropriate for the client platform and which honor the allowed
+   providers specified in CONFIG.  Allocate providers from POOL.  */
+svn_error_t *
+svn_auth__get_masterpass_providers(apr_array_header_t **providers,
+                                   svn_config_t *config,
+                                   apr_pool_t *pool);
+
+#if !defined(WIN32)
+/* Set *PROVIDER to a master passphrase provider which uses the GPG
+   Agent for storage/retrieval.  */
+void svn_auth__get_gpg_agent_masterpass_provider(
+    svn_auth__masterpass_provider_object_t **provider,
+    apr_pool_t *pool);
+#endif /* !defined(WIN32) */
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */

Modified: subversion/branches/master-passphrase/subversion/libsvn_subr/auth.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_subr/auth.c?rev=1311958&r1=1311957&r2=1311958&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_subr/auth.c 
(original)
+++ subversion/branches/master-passphrase/subversion/libsvn_subr/auth.c Tue Apr 
10 19:46:12 2012
@@ -660,192 +660,3 @@ svn_auth_get_platform_specific_client_pr
 
   return SVN_NO_ERROR;
 }
-
-
-/*** Master Passphrase ***/
-
-#define AUTHN_MASTER_PASS_KNOWN_TEXT  "Subversion"
-#define AUTHN_FAUX_REALMSTRING        "localhost.localdomain"
-#define AUTHN_CHECKTEXT_KEY           "checktext"
-#define AUTHN_PASSTYPE_KEY            "passtype"
-
-
-/* Use SECRET to encrypt TEXT, returning the result (allocated from
-   RESULT_POOL) in *CRYPT_TEXT.  Use SCRATCH_POOL for temporary
-   allocations. */
-static svn_error_t *
-encrypt_text(const svn_string_t **crypt_text,
-             const svn_string_t *text,
-             const char *secret,
-             apr_pool_t *result_pool,
-             apr_pool_t *scratch_pool)
-{
-  /* ### FIXME!  This a mindless temporary implementation, offering
-         all the security and privacy of a glass bathroom!  ***/
-
-  SVN_ERR_ASSERT(text && text->data);
-  SVN_ERR_ASSERT(secret);
-
-  *crypt_text = svn_base64_encode_string2(svn_string_createf(scratch_pool,
-                                                             "%s+%s",
-                                                             text->data,
-                                                             secret),
-                                          FALSE, result_pool);
-  return SVN_NO_ERROR;
-}
-
-
-/* Use SECRET to decrypt CRYPT_TEXT, returning the result (allocated
-   from RESULT_POOL) in *TEXT.  Use SCRATCH_POOL for temporary
-   allocations. */
-static svn_error_t *
-decrypt_text(const svn_string_t **text,
-             const svn_string_t *crypt_text,
-             const char *secret,
-             apr_pool_t *result_pool,
-             apr_pool_t *scratch_pool)
-{
-  /* ### FIXME!  This a mindless temporary implementation, offering
-         all the security and privacy of a glass bathroom!  ***/
-
-  const svn_string_t *work_text;
-  int secret_len, text_len;
-
-  SVN_ERR_ASSERT(crypt_text && crypt_text->data);
-  SVN_ERR_ASSERT(secret);
-
-  secret_len = strlen(secret);
-  work_text = svn_base64_decode_string(crypt_text, scratch_pool);
-  if (work_text->len < (secret_len + 1))
-    return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL,
-                            "Invalid master passphrase.");
-  text_len = work_text->len - secret_len - 1;
-  if (work_text->data[text_len] != '+')
-    return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL,
-                            "Invalid master passphrase.");
-  if (strcmp(work_text->data + text_len + 1, secret) != 0)
-    return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL,
-                            "Invalid master passphrase.");
-  *text = svn_string_ncreate(work_text->data,
-                             work_text->len - secret_len - 1,
-                             result_pool);
-  return SVN_NO_ERROR;
-}
-             
-
-svn_error_t *
-svn_auth_master_passphrase_get(const char **passphrase,
-                               svn_auth_baton_t *auth_baton,
-                               apr_pool_t *result_pool,
-                               apr_pool_t *scratch_pool)
-{
-  apr_hash_t *creds_hash;
-  const svn_string_t *check_text;
-  const char *config_dir = apr_hash_get(auth_baton->parameters,
-                                        SVN_AUTH_PARAM_CONFIG_DIR,
-                                        APR_HASH_KEY_STRING);
-#ifdef SVN_AUTH_TEMP_USE_FAUX_PASSPHRASE
-  const char *default_passphrase = SVN_AUTH_TEMP_MASTER_PASSPHRASE;
-#else
-  const char *default_passphrase =
-    apr_hash_get(auth_baton->parameters,
-                 SVN_AUTH_PARAM_DEFAULT_MASTER_PASSPHRASE,
-                 APR_HASH_KEY_STRING);
-#endif
-
-  /* Read the existing passphrase storage record so we can validate
-     any master passphrase we have or fetch. If there's no check text,
-     we must assume that there's no global master passphrase set, so
-     we'll just return that fact. */
-  SVN_ERR(svn_config_read_auth_data(&creds_hash,
-                                    SVN_AUTH_CRED_MASTER_PASSPHRASE,
-                                    AUTHN_FAUX_REALMSTRING, config_dir,
-                                    scratch_pool));
-  check_text = apr_hash_get(creds_hash, AUTHN_CHECKTEXT_KEY,
-                            APR_HASH_KEY_STRING);
-  if (! check_text)
-    {
-      *passphrase = NULL;
-      return SVN_NO_ERROR;
-    }
-
-  /* If there's a default passphrase, verify that it matches the
-     stored known-text.  */
-  if (default_passphrase)
-    {
-      const svn_string_t *crypt_text;
-      SVN_ERR(encrypt_text(&crypt_text,
-                           svn_string_create(AUTHN_MASTER_PASS_KNOWN_TEXT,
-                                             scratch_pool),
-                           default_passphrase, scratch_pool, scratch_pool));
-      if (svn_string_compare(crypt_text, check_text))
-        {
-          *passphrase = apr_pstrdup(result_pool, default_passphrase);
-          return SVN_NO_ERROR;
-        }
-      default_passphrase = NULL;
-    }
-
-  /* We do not yet know the master passphrase, so we need to consult
-     the providers.  */
-  /* ### TODO ### */
-
-  default_passphrase = NULL;
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_auth_master_passphrase_set(svn_auth_baton_t *auth_baton,
-                               const char *new_passphrase,
-                               apr_pool_t *scratch_pool)
-{
-  apr_hash_t *creds_hash;
-  const char *config_dir = apr_hash_get(auth_baton->parameters,
-                                        SVN_AUTH_PARAM_CONFIG_DIR,
-                                        APR_HASH_KEY_STRING);
-  const char *old_passphrase;
-  const svn_string_t *old_check_text, *new_check_text;
-
-  /* First, fetch the existing passphrase. */
-  SVN_ERR(svn_auth_master_passphrase_get(&old_passphrase, auth_baton,
-                                         scratch_pool, scratch_pool));
-
-  /* Now, read the existing passphrase storage record and grab the
-     current checkidentify. */
-  SVN_ERR(svn_config_read_auth_data(&creds_hash,
-                                    SVN_AUTH_CRED_MASTER_PASSPHRASE,
-                                    AUTHN_FAUX_REALMSTRING, config_dir,
-                                    scratch_pool));
-  old_check_text = apr_hash_get(creds_hash, AUTHN_CHECKTEXT_KEY,
-                                APR_HASH_KEY_STRING);
-
-  SVN_ERR(svn_config_write_auth_data(creds_hash,
-                                     SVN_AUTH_CRED_MASTER_PASSPHRASE,
-                                     AUTHN_FAUX_REALMSTRING, config_dir,
-                                     scratch_pool));
-
-  if (new_passphrase)
-    {
-      /* Encrypt the known text with NEW_PASSPHRASE to form the crypttext,
-         and stuff that into the CREDS_HASH. */
-      SVN_ERR(encrypt_text(&new_check_text,
-                           svn_string_create(AUTHN_MASTER_PASS_KNOWN_TEXT,
-                                             scratch_pool),
-                           new_passphrase, scratch_pool, scratch_pool));
-      apr_hash_set(creds_hash, AUTHN_CHECKTEXT_KEY,
-                   APR_HASH_KEY_STRING, new_check_text);
-    }
-  else
-    {
-      apr_hash_set(creds_hash, AUTHN_CHECKTEXT_KEY, APR_HASH_KEY_STRING, NULL);
-    }
-
-  /* Re-encrypt all stored credentials in light of NEW_PASSPHRASE. */
-  /* ### TODO ### */
-
-  /* Save credentials to disk. */
-  return svn_config_write_auth_data(creds_hash,
-                                    SVN_AUTH_CRED_MASTER_PASSPHRASE,
-                                    AUTHN_FAUX_REALMSTRING, config_dir,
-                                    scratch_pool);
-}

Modified: 
subversion/branches/master-passphrase/subversion/libsvn_subr/gpg_agent.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_subr/gpg_agent.c?rev=1311958&r1=1311957&r2=1311958&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_subr/gpg_agent.c 
(original)
+++ subversion/branches/master-passphrase/subversion/libsvn_subr/gpg_agent.c 
Tue Apr 10 19:46:12 2012
@@ -152,17 +152,29 @@ send_option(int sd, char *buf, size_t n,
   return (strncmp(buf, "OK", 2) == 0);
 }
 
+/* Create the CACHE_ID which will be generated based on REALMSTRING
+   (similar to other password caching mechanisms). */
+static const char *
+cacheid_from_realmstring(const char *realmstring,
+                         apr_pool_t *pool)
+{
+  svn_checksum_t *digest = svn_checksum_create(svn_checksum_md5, pool);
+  svn_checksum(&digest, svn_checksum_md5, realmstring,
+               strlen(realmstring), pool);
+  return svn_checksum_to_cstring(digest, pool);
+}
+
+
 /* Implementation of svn_auth__password_get_t that retrieves the password
    from gpg-agent */
 static svn_error_t *
-password_get_gpg_agent(svn_boolean_t *done,
-                       const char **password,
-                       apr_hash_t *creds,
-                       const char *realmstring,
-                       const char *username,
-                       apr_hash_t *parameters,
-                       svn_boolean_t non_interactive,
-                       apr_pool_t *pool)
+password_get_gpg_agent_helper(svn_boolean_t *done,
+                              const char **password,
+                              const char *password_prompt,
+                              const char *realm_prompt,
+                              const char *cache_id,
+                              svn_boolean_t non_interactive,
+                              apr_pool_t *pool)
 {
   int sd;
   char *gpg_agent_info = NULL;
@@ -172,16 +184,12 @@ password_get_gpg_agent(svn_boolean_t *do
 
   apr_array_header_t *socket_details;
   const char *request = NULL;
-  const char *cache_id = NULL;
   struct sockaddr_un addr;
   const char *tty_name;
   const char *tty_type;
   const char *lc_ctype;
   const char *display;
   const char *socket_name = NULL;
-  svn_checksum_t *digest = NULL;
-  char *password_prompt;
-  char *realm_prompt;
 
   *done = FALSE;
 
@@ -330,23 +338,11 @@ password_get_gpg_agent(svn_boolean_t *do
         }
     }
 
-  /* Create the CACHE_ID which will be generated based on REALMSTRING similar
-     to other password caching mechanisms. */
-  digest = svn_checksum_create(svn_checksum_md5, pool);
-  svn_checksum(&digest, svn_checksum_md5, realmstring, strlen(realmstring),
-               pool);
-  cache_id = svn_checksum_to_cstring(digest, pool);
-
-  password_prompt = apr_psprintf(pool, _("Password for '%s': "), username);
-  realm_prompt = apr_psprintf(pool, _("Enter your Subversion password for %s"),
-                              realmstring);
   request = apr_psprintf(pool,
                          "GET_PASSPHRASE --data %s--repeat=1 "
                          "%s X %s %s\n",
                          non_interactive ? "--no-ask " : "",
-                         cache_id,
-                         escape_blanks(password_prompt),
-                         escape_blanks(realm_prompt));
+                         cache_id, password_prompt, realm_prompt);
 
   if (write(sd, request, strlen(request)) == -1)
     {
@@ -382,6 +378,35 @@ password_get_gpg_agent(svn_boolean_t *do
 }
 
 
+/* Implementation of svn_auth__password_get_t that retrieves the password
+   from gpg-agent */
+static svn_error_t *
+password_get_gpg_agent(svn_boolean_t *done,
+                       const char **password,
+                       apr_hash_t *creds,
+                       const char *realmstring,
+                       const char *username,
+                       apr_hash_t *parameters,
+                       svn_boolean_t non_interactive,
+                       apr_pool_t *pool)
+{
+  const char *cache_id = cacheid_from_realmstring(realmstring, pool);
+  char *password_prompt =
+    apr_psprintf(pool, _("Password for '%s': "), username);
+  char *realm_prompt =
+    apr_psprintf(pool, _("Enter your Subversion password for %s"), 
realmstring);
+
+  escape_blanks(realm_prompt);
+  escape_blanks(password_prompt);
+
+  return svn_error_trace(password_get_gpg_agent_helper(done, password,
+                                                       cache_id,
+                                                       password_prompt,
+                                                       realm_prompt,
+                                                       non_interactive,
+                                                       pool));
+}
+
 /* Implementation of svn_auth__password_set_t that would store the
    password in GPG Agent if that's how this particular integration
    worked.  But it isn't.  GPG Agent stores the password provided by
@@ -457,5 +482,65 @@ svn_auth_get_gpg_agent_simple_provider(s
   *provider = po;
 }
 
+
+
+
+/*-----------------------------------------------------------------------*/
+/* GPG Agent master passphrase.                                          */
+/*-----------------------------------------------------------------------*/
+
+/* Implements svn_auth__masterpass_fetch_t. */
+static svn_error_t *
+gpg_agent_masterpass_fetch(const char **passphrase,
+                           svn_boolean_t non_interactive,
+                           void *provider_baton,
+                           apr_pool_t *pool)
+{
+  const char *cache_id = "Subversion Master Password";
+  const char *password_prompt = _("Password:");
+  const char *realm_prompt = _("Enter+your+Subversion+master+password");
+  svn_boolean_t done;
+  const char *password;
+  svn_checksum_t *digest;
+  
+  SVN_ERR(password_get_gpg_agent_helper(&done, &password, cache_id,
+                                        password_prompt, realm_prompt,
+                                        non_interactive, pool));
+
+  /* ### FIXME: Should be SHA-256 */
+  svn_checksum(&digest, svn_checksum_sha1, password, strlen(password), pool);
+  *passphrase = svn_checksum_to_cstring_display(digest, pool);
+  return SVN_NO_ERROR;
+}
+
+/* Implements svn_auth__masterpass_store_t. */
+static svn_error_t *
+gpg_agent_masterpass_store(const char *passphrase,
+                           svn_boolean_t non_interactive,
+                           void *provider_baton,
+                           apr_pool_t *pool)
+{
+  return SVN_NO_ERROR;
+}
+
+static const svn_auth__masterpass_provider_t
+gpg_agent_masterpass_provider = {
+  gpg_agent_masterpass_fetch,
+  gpg_agent_masterpass_store
+};
+
+/* Public API */
+void
+svn_auth__get_gpg_agent_masterpass_provider(
+    svn_auth__masterpass_provider_object_t **provider,
+    apr_pool_t *pool)
+{
+  svn_auth__masterpass_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
+
+  po->vtable = &gpg_agent_masterpass_provider;
+  *provider = po;
+}
+
+
 #endif /* SVN_HAVE_GPG_AGENT */
 #endif /* !WIN32 */

Added: subversion/branches/master-passphrase/subversion/libsvn_subr/masterpass.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_subr/masterpass.c?rev=1311958&view=auto
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_subr/masterpass.c 
(added)
+++ subversion/branches/master-passphrase/subversion/libsvn_subr/masterpass.c 
Tue Apr 10 19:46:12 2012
@@ -0,0 +1,390 @@
+/*
+ * masterpass.c: master passphrase support functions for Subversion
+ *
+ * ====================================================================
+ *    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.
+ * ====================================================================
+ */
+
+
+#include <apr_pools.h>
+#include <apr_tables.h>
+#include <apr_strings.h>
+
+#include "svn_string.h"
+#include "svn_error.h"
+#include "svn_auth.h"
+#include "svn_config.h"
+#include "svn_private_config.h"
+#include "svn_dso.h"
+#include "svn_base64.h"
+#include "svn_version.h"
+#include "private/svn_auth_private.h"
+
+#include "crypto.h"
+
+static svn_error_t *
+get_masterpass_provider(svn_auth__masterpass_provider_object_t **provider,
+                        const char *provider_name,
+                        apr_pool_t *pool)
+{
+  *provider = NULL;
+
+  if (apr_strnatcmp(provider_name, "gnome_keyring") == 0 ||
+      apr_strnatcmp(provider_name, "kwallet") == 0)
+    {
+#if defined(SVN_HAVE_GNOME_KEYRING) || defined(SVN_HAVE_KWALLET)
+      apr_dso_handle_t *dso;
+      apr_dso_handle_sym_t provider_function_symbol, version_function_symbol;
+      const char *library_label, *library_name;
+      const char *provider_function_name, *version_function_name;
+      library_name = apr_psprintf(pool,
+                                  "libsvn_auth_%s-%d.so.0",
+                                  provider_name,
+                                  SVN_VER_MAJOR);
+      library_label = apr_psprintf(pool, "svn_%s", provider_name);
+      provider_function_name =
+        apr_psprintf(pool, "svn_auth_get_%s_masterpass_provider",
+                     provider_name);
+      version_function_name = apr_psprintf(pool, "svn_auth_%s_version",
+                                           provider_name);
+      SVN_ERR(svn_dso_load(&dso, library_name));
+      if (dso)
+        {
+          if (apr_dso_sym(&version_function_symbol,
+                          dso,
+                          version_function_name) == 0)
+            {
+              svn_version_func_t version_function
+                = version_function_symbol;
+              const svn_version_checklist_t check_list[] =
+                {
+                  { library_label, version_function },
+                  { NULL, NULL }
+                };
+              SVN_ERR(svn_ver_check_list(svn_subr_version(), check_list));
+            }
+          if (apr_dso_sym(&provider_function_symbol,
+                          dso,
+                          provider_function_name) == 0)
+            {
+              svn_auth__masterpass_provider_func_t provider_function =
+                provider_function_symbol;
+              provider_function(provider, pool);
+            }
+        }
+#endif
+    }
+  else
+    {
+#if defined(SVN_HAVE_GPG_AGENT)
+      if (strcmp(provider_name, "gpg_agent") == 0)
+        {
+          svn_auth__get_gpg_agent_masterpass_provider(provider, pool);
+        }
+#endif
+#ifdef SVN_HAVE_KEYCHAIN_SERVICES
+      /* ### TODO: Implement MacOS X Keychain provider. */
+#endif
+#if defined(WIN32) && !defined(__MINGW32__)
+      /* ### TODO: Can Windows actually use this?  It's less of a
+         ### store and more of a service.  */
+#endif
+    }
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_auth__get_masterpass_providers(apr_array_header_t **providers,
+                                   svn_config_t *config,
+                                   apr_pool_t *pool)
+{
+  svn_auth__masterpass_provider_object_t *provider;
+  const char *password_stores_config_option;
+  apr_array_header_t *password_stores;
+  int i;
+
+#define SVN__DEFAULT_AUTH_PROVIDER_LIST \
+         "gnome-keyring,kwallet,keychain,gpg-agent,windows-cryptoapi"
+
+  if (config)
+    {
+      svn_config_get(config, &password_stores_config_option,
+                     SVN_CONFIG_SECTION_AUTH,
+                     SVN_CONFIG_OPTION_PASSWORD_STORES,
+                     SVN__DEFAULT_AUTH_PROVIDER_LIST);
+    }
+  else
+    {
+      password_stores_config_option = SVN__DEFAULT_AUTH_PROVIDER_LIST;
+    }
+
+  *providers = apr_array_make(pool, 12, sizeof(svn_auth_provider_object_t *));
+
+  password_stores = svn_cstring_split(password_stores_config_option,
+                                      " ,", TRUE, pool);
+
+  for (i = 0; i < password_stores->nelts; i++)
+    {
+      const char *password_store =
+        APR_ARRAY_IDX(password_stores, i, const char *);
+
+      /* GNOME Keyring */
+      if (apr_strnatcmp(password_store, "gnome-keyring") == 0)
+        {
+          SVN_ERR(get_masterpass_provider(&provider, "gnome_keyring", pool));
+          if (provider)
+            APR_ARRAY_PUSH(*providers,
+                           svn_auth__masterpass_provider_object_t *) = 
provider;
+          continue;
+        }
+
+      /* GPG-AGENT */
+      if (apr_strnatcmp(password_store, "gpg-agent") == 0)
+        {
+          SVN_ERR(get_masterpass_provider(&provider, "gpg_agent", pool));
+          if (provider)
+            APR_ARRAY_PUSH(*providers,
+                           svn_auth__masterpass_provider_object_t *) = 
provider;
+          continue;
+        }
+
+      /* KWallet */
+      if (apr_strnatcmp(password_store, "kwallet") == 0)
+        {
+          SVN_ERR(get_masterpass_provider(&provider, "kwallet", pool));
+          if (provider)
+            APR_ARRAY_PUSH(*providers,
+                           svn_auth__masterpass_provider_object_t *) = 
provider;
+          continue;
+        }
+
+      /* Keychain */
+      if (apr_strnatcmp(password_store, "keychain") == 0)
+        {
+          SVN_ERR(get_masterpass_provider(&provider, "keychain", pool));
+          if (provider)
+            APR_ARRAY_PUSH(*providers,
+                           svn_auth__masterpass_provider_object_t *) = 
provider;
+          continue;
+        }
+
+      /* Windows */
+      if (apr_strnatcmp(password_store, "windows-cryptoapi") == 0)
+        {
+          SVN_ERR(get_masterpass_provider(&provider, "windows", pool));
+          if (provider)
+            APR_ARRAY_PUSH(*providers,
+                           svn_auth__masterpass_provider_object_t *) = 
provider;
+          continue;
+        }
+
+      return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
+                               _("Invalid config: unknown password store 
'%s'"),
+                               password_store);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
+/*** Master Passphrase ***/
+
+#define AUTHN_MASTER_PASS_KNOWN_TEXT  "Subversion"
+#define AUTHN_FAUX_REALMSTRING        "localhost.localdomain"
+#define AUTHN_CHECKTEXT_KEY           "checktext"
+#define AUTHN_PASSTYPE_KEY            "passtype"
+
+
+/* Use SECRET to encrypt TEXT, returning the result (allocated from
+   RESULT_POOL) in *CRYPT_TEXT.  Use SCRATCH_POOL for temporary
+   allocations. */
+static svn_error_t *
+encrypt_text(const svn_string_t **crypt_text,
+             const svn_string_t *text,
+             const char *secret,
+             apr_pool_t *result_pool,
+             apr_pool_t *scratch_pool)
+{
+  /* ### FIXME!  This a mindless temporary implementation, offering
+         all the security and privacy of a glass bathroom!  ***/
+
+  SVN_ERR_ASSERT(text && text->data);
+  SVN_ERR_ASSERT(secret);
+
+  *crypt_text = svn_base64_encode_string2(svn_string_createf(scratch_pool,
+                                                             "%s+%s",
+                                                             text->data,
+                                                             secret),
+                                          FALSE, result_pool);
+  return SVN_NO_ERROR;
+}
+
+
+/* Use SECRET to decrypt CRYPT_TEXT, returning the result (allocated
+   from RESULT_POOL) in *TEXT.  Use SCRATCH_POOL for temporary
+   allocations. */
+static svn_error_t *
+decrypt_text(const svn_string_t **text,
+             const svn_string_t *crypt_text,
+             const char *secret,
+             apr_pool_t *result_pool,
+             apr_pool_t *scratch_pool)
+{
+  /* ### FIXME!  This a mindless temporary implementation, offering
+         all the security and privacy of a glass bathroom!  ***/
+
+  const svn_string_t *work_text;
+  int secret_len, text_len;
+
+  SVN_ERR_ASSERT(crypt_text && crypt_text->data);
+  SVN_ERR_ASSERT(secret);
+
+  secret_len = strlen(secret);
+  work_text = svn_base64_decode_string(crypt_text, scratch_pool);
+  if (work_text->len < (secret_len + 1))
+    return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL,
+                            "Invalid master passphrase.");
+  text_len = work_text->len - secret_len - 1;
+  if (work_text->data[text_len] != '+')
+    return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL,
+                            "Invalid master passphrase.");
+  if (strcmp(work_text->data + text_len + 1, secret) != 0)
+    return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL,
+                            "Invalid master passphrase.");
+  *text = svn_string_ncreate(work_text->data,
+                             work_text->len - secret_len - 1,
+                             result_pool);
+  return SVN_NO_ERROR;
+}
+             
+
+svn_error_t *
+svn_auth_master_passphrase_get(const char **passphrase,
+                               svn_auth_baton_t *auth_baton,
+                               apr_pool_t *result_pool,
+                               apr_pool_t *scratch_pool)
+{
+  apr_hash_t *creds_hash;
+  const svn_string_t *check_text;
+  const char *config_dir = svn_auth_get_parameter(auth_baton,
+                                                  SVN_AUTH_PARAM_CONFIG_DIR);
+#ifdef SVN_AUTH_TEMP_USE_FAUX_PASSPHRASE
+  const char *default_passphrase = SVN_AUTH_TEMP_MASTER_PASSPHRASE;
+#else
+  const char *default_passphrase =
+    svn_auth_get_parameter(auth_baton,
+                           SVN_AUTH_PARAM_DEFAULT_MASTER_PASSPHRASE);
+#endif
+
+  /* Read the existing passphrase storage record so we can validate
+     any master passphrase we have or fetch. If there's no check text,
+     we must assume that there's no global master passphrase set, so
+     we'll just return that fact. */
+  SVN_ERR(svn_config_read_auth_data(&creds_hash,
+                                    SVN_AUTH_CRED_MASTER_PASSPHRASE,
+                                    AUTHN_FAUX_REALMSTRING, config_dir,
+                                    scratch_pool));
+  check_text = apr_hash_get(creds_hash, AUTHN_CHECKTEXT_KEY,
+                            APR_HASH_KEY_STRING);
+  if (! check_text)
+    {
+      *passphrase = NULL;
+      return SVN_NO_ERROR;
+    }
+
+  /* If there's a default passphrase, verify that it matches the
+     stored known-text.  */
+  if (default_passphrase)
+    {
+      const svn_string_t *crypt_text;
+      SVN_ERR(encrypt_text(&crypt_text,
+                           svn_string_create(AUTHN_MASTER_PASS_KNOWN_TEXT,
+                                             scratch_pool),
+                           default_passphrase, scratch_pool, scratch_pool));
+      if (svn_string_compare(crypt_text, check_text))
+        {
+          *passphrase = apr_pstrdup(result_pool, default_passphrase);
+          return SVN_NO_ERROR;
+        }
+      default_passphrase = NULL;
+    }
+
+  /* We do not yet know the master passphrase, so we need to consult
+     the providers.  */
+  /* ### TODO ### */
+
+  default_passphrase = NULL;
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_auth_master_passphrase_set(svn_auth_baton_t *auth_baton,
+                               const char *new_passphrase,
+                               apr_pool_t *scratch_pool)
+{
+  apr_hash_t *creds_hash;
+  const char *config_dir = svn_auth_get_parameter(auth_baton,
+                                                  SVN_AUTH_PARAM_CONFIG_DIR);
+  const char *old_passphrase;
+  const svn_string_t *old_check_text, *new_check_text;
+
+  /* First, fetch the existing passphrase. */
+  SVN_ERR(svn_auth_master_passphrase_get(&old_passphrase, auth_baton,
+                                         scratch_pool, scratch_pool));
+
+  /* Now, read the existing passphrase storage record and grab the
+     current checkidentify. */
+  SVN_ERR(svn_config_read_auth_data(&creds_hash,
+                                    SVN_AUTH_CRED_MASTER_PASSPHRASE,
+                                    AUTHN_FAUX_REALMSTRING, config_dir,
+                                    scratch_pool));
+  old_check_text = apr_hash_get(creds_hash, AUTHN_CHECKTEXT_KEY,
+                                APR_HASH_KEY_STRING);
+
+  SVN_ERR(svn_config_write_auth_data(creds_hash,
+                                     SVN_AUTH_CRED_MASTER_PASSPHRASE,
+                                     AUTHN_FAUX_REALMSTRING, config_dir,
+                                     scratch_pool));
+
+  if (new_passphrase)
+    {
+      /* Encrypt the known text with NEW_PASSPHRASE to form the crypttext,
+         and stuff that into the CREDS_HASH. */
+      SVN_ERR(encrypt_text(&new_check_text,
+                           svn_string_create(AUTHN_MASTER_PASS_KNOWN_TEXT,
+                                             scratch_pool),
+                           new_passphrase, scratch_pool, scratch_pool));
+      apr_hash_set(creds_hash, AUTHN_CHECKTEXT_KEY,
+                   APR_HASH_KEY_STRING, new_check_text);
+    }
+  else
+    {
+      apr_hash_set(creds_hash, AUTHN_CHECKTEXT_KEY, APR_HASH_KEY_STRING, NULL);
+    }
+
+  /* Re-encrypt all stored credentials in light of NEW_PASSPHRASE. */
+  /* ### TODO ### */
+
+  /* Save credentials to disk. */
+  return svn_config_write_auth_data(creds_hash,
+                                    SVN_AUTH_CRED_MASTER_PASSPHRASE,
+                                    AUTHN_FAUX_REALMSTRING, config_dir,
+                                    scratch_pool);
+}

Propchange: 
subversion/branches/master-passphrase/subversion/libsvn_subr/masterpass.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
subversion/branches/master-passphrase/subversion/libsvn_subr/masterpass.c
------------------------------------------------------------------------------
    svn:mime-type = text/x-csrc


Reply via email to