You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by cm...@apache.org on 2012/03/23 19:16:04 UTC

svn commit: r1304538 - in /subversion/branches/master-passphrase/subversion: include/svn_auth.h libsvn_subr/auth.c libsvn_subr/config_file.c svn/changepassword-cmd.c svn/cl.h svn/main.c

Author: cmpilato
Date: Fri Mar 23 18:16:04 2012
New Revision: 1304538

URL: http://svn.apache.org/viewvc?rev=1304538&view=rev
Log:
On the 'master-passphrase' branch, add an 'svn changepassword'
subcommand and the beginnings of some functional support thereof.

* subversion/include/svn_auth.h
  (SVN_AUTH_CRED_MASTER_PASSPHRASE): New credential type #define.
  (svn_auth_cred_master_passphrase_t): New credential type structure.
    Currently unused.
  (SVN_AUTH_TEMP_USE_FAUX_PASSPHRASE, SVN_AUTH_TEMP_MASTER_PASSPHRASE):
    Temporary #defines used while playing with this implementation to
    ensure a fixed passphrase is in place.
  (svn_auth_master_passphrase_get, svn_auth_master_passphrase_set): Public
    functions for getting and setting, respectively, the master passphrase.
  (SVN_AUTH_PARAM_DEFAULT_MASTER_PASSPHRASE): New auth baton parameter
    key #define.

* subversion/libsvn_subr/config_file.c
  (ensure_auth_dirs): Ensure that the 'svn.masterpassphrase' authn
    directory gets created.

* subversion/libsvn_subr/auth.c
  (AUTHN_MASTER_PASS_KNOWN_TEXT, AUTHN_FAUX_REALMSTRING, AUTHN_CHECKTEXT_KEY,
   AUTHN_PASSTYPE_KEY): New local #defines.
  (encrypt_text, decrypt_text): New helper functions.
  (svn_auth_master_passphrase_get, svn_auth_master_passphrase_set): Public
    functions for getting and setting, respectively, the master passphrase.

* subversion/svn/changepassword-cmd.c
  New source file, containing svn_cl__changepassword() implementation.

* subversion/svn/cl.h
  (svn_cl__changepassword): New function prototype.

* subversion/svn/main.c
  (svn_cl__options): Generalize the usage message for --remove a bit.
  (svn_cl__cmd_table): Add record for new "changepassword" subcommand.

Added:
    subversion/branches/master-passphrase/subversion/svn/changepassword-cmd.c   (with props)
Modified:
    subversion/branches/master-passphrase/subversion/include/svn_auth.h
    subversion/branches/master-passphrase/subversion/libsvn_subr/auth.c
    subversion/branches/master-passphrase/subversion/libsvn_subr/config_file.c
    subversion/branches/master-passphrase/subversion/svn/cl.h
    subversion/branches/master-passphrase/subversion/svn/main.c

Modified: subversion/branches/master-passphrase/subversion/include/svn_auth.h
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/include/svn_auth.h?rev=1304538&r1=1304537&r2=1304538&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/include/svn_auth.h (original)
+++ subversion/branches/master-passphrase/subversion/include/svn_auth.h Fri Mar 23 18:16:04 2012
@@ -340,6 +340,58 @@ typedef struct svn_auth_cred_ssl_server_
 } svn_auth_cred_ssl_server_trust_t;
 
 
+/** Master passphrase credential type.
+ * 
+ * This is a special type of credential used locally only, not as part
+ * of any server-related challenge.
+ *
+ * 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_MASTER_PASSPHRASE "svn.masterpassphrase"
+
+typedef struct svn_auth_cred_master_passphrase_t
+{
+  /** Master passphrase */
+  const char *passphrase;
+  /** Indicates if the credentials may be saved (to disk). For example, a
+   * GUI prompt implementation with a remember password checkbox shall set
+   * @a may_save to TRUE if the checkbox is checked.
+   */
+  svn_boolean_t may_save;
+} svn_auth_cred_master_passphrase_t;
+
+/* Temporary support for a hard-coded master passphrase.  (Truth told,
+   cmpilato just doesn't feel like implementing prompting providers
+   and stuff just yet.)  */
+#define SVN_AUTH_TEMP_USE_FAUX_PASSPHRASE 1
+#define SVN_AUTH_TEMP_MASTER_PASSPHRASE "mypass"
+
+/* Set *PASSPHRASE to the master passphrase for authentication
+   credentials stored in the runtime configuration associated with
+   AUTH_BATON, allocated from RESULT_POOL.  Use SCRATCH_POOL for
+   temporary allocations. */
+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);
+
+/* Set the master passphrase for authentication credentials stored in the
+   runtime configuration associated with AUTH_BATON to NEW_PASSPHRASE
+   (which may be NULL to remove an existing passphrase).  Use
+   SCRATCH_POOL for necessary allocation.  */
+svn_error_t *
+svn_auth_master_passphrase_set(svn_auth_baton_t *auth_baton,
+                               const char *new_passphrase,
+                               apr_pool_t *scratch_pool);
+
 
 /** Credential-constructing prompt functions. **/
 
@@ -577,6 +629,12 @@ svn_auth_get_parameter(svn_auth_baton_t 
 #define SVN_AUTH_PARAM_DEFAULT_PASSWORD  SVN_AUTH_PARAM_PREFIX "password"
 /** @} */
 
+/** @brief Cached value of the master passphrase.
+ * @since New in 1.7.
+ */
+#define SVN_AUTH_PARAM_DEFAULT_MASTER_PASSPHRASE  SVN_AUTH_PARAM_PREFIX \
+                                                      "master-passphrase"
+
 /** @brief The application doesn't want any providers to prompt
  * users. Property value is irrelevant; only property's existence
  * matters. */

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=1304538&r1=1304537&r2=1304538&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_subr/auth.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_subr/auth.c Fri Mar 23 18:16:04 2012
@@ -33,6 +33,7 @@
 #include "svn_config.h"
 #include "svn_private_config.h"
 #include "svn_dso.h"
+#include "svn_base64.h"
 #include "svn_version.h"
 
 /* AN OVERVIEW
@@ -659,3 +660,192 @@ 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/config_file.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_subr/config_file.c?rev=1304538&r1=1304537&r2=1304538&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_subr/config_file.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_subr/config_file.c Fri Mar 23 18:16:04 2012
@@ -504,6 +504,7 @@ ensure_auth_dirs(const char *path,
 
   /* If a provider exists that wants to store credentials in
      ~/.subversion, a subdirectory for the cred_kind must exist. */
+  ensure_auth_subdir(auth_dir, SVN_AUTH_CRED_MASTER_PASSPHRASE, pool);
   ensure_auth_subdir(auth_dir, SVN_AUTH_CRED_SIMPLE, pool);
   ensure_auth_subdir(auth_dir, SVN_AUTH_CRED_USERNAME, pool);
   ensure_auth_subdir(auth_dir, SVN_AUTH_CRED_SSL_SERVER_TRUST, pool);

Added: subversion/branches/master-passphrase/subversion/svn/changepassword-cmd.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/svn/changepassword-cmd.c?rev=1304538&view=auto
==============================================================================
--- subversion/branches/master-passphrase/subversion/svn/changepassword-cmd.c (added)
+++ subversion/branches/master-passphrase/subversion/svn/changepassword-cmd.c Fri Mar 23 18:16:04 2012
@@ -0,0 +1,70 @@
+/*
+ * changepassword-cmd.c -- Associate (or deassociate) a master password
+ *                         with the local authentication credential cache.
+ *
+ * ====================================================================
+ *    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 "svn_client.h"
+#include "svn_error.h"
+#include "svn_auth.h"
+
+#include "cl.h"
+
+
+
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__changepassword(apr_getopt_t *os,
+                       void *baton,
+                       apr_pool_t *pool)
+{
+  svn_cl__cmd_baton_t *cmd_baton = baton;
+  svn_cl__opt_state_t *opt_state = cmd_baton->opt_state;
+  svn_auth_baton_t *auth_baton = cmd_baton->ctx->auth_baton;
+  apr_array_header_t *args;
+  const char *new_password = NULL;
+
+  SVN_ERR(svn_opt_parse_all_args(&args, os, pool));
+
+  /* If we're not removing changepasswords, then our only argument should
+     be the new password; otherwise, there should be no arguments. */
+  if (! opt_state->remove)
+    {
+      if (args->nelts < 1)
+        return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+      if (args->nelts > 1)
+        return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
+#ifdef SVN_AUTH_TEMP_USE_FAUX_PASSPHRASE
+      new_password = SVN_AUTH_TEMP_MASTER_PASSPHRASE;
+#else
+      new_password = APR_ARRAY_IDX(args, 0, const char *);
+#endif
+    }
+  else
+    {
+      if (args->nelts > 0)
+        return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
+    }
+
+  SVN_ERR(svn_auth_master_passphrase_set(auth_baton, new_password, pool));
+  return SVN_NO_ERROR;
+}

Propchange: subversion/branches/master-passphrase/subversion/svn/changepassword-cmd.c
------------------------------------------------------------------------------
    svn:eol-style = native

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

Modified: subversion/branches/master-passphrase/subversion/svn/cl.h
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/svn/cl.h?rev=1304538&r1=1304537&r2=1304538&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/svn/cl.h (original)
+++ subversion/branches/master-passphrase/subversion/svn/cl.h Fri Mar 23 18:16:04 2012
@@ -250,6 +250,7 @@ svn_opt_subcommand_t
   svn_cl__blame,
   svn_cl__cat,
   svn_cl__changelist,
+  svn_cl__changepassword,
   svn_cl__checkout,
   svn_cl__cleanup,
   svn_cl__commit,

Modified: subversion/branches/master-passphrase/subversion/svn/main.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/svn/main.c?rev=1304538&r1=1304537&r2=1304538&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/svn/main.c (original)
+++ subversion/branches/master-passphrase/subversion/svn/main.c Fri Mar 23 18:16:04 2012
@@ -283,7 +283,7 @@ const apr_getopt_option_t svn_cl__option
   {"limit",         'l', 1, N_("maximum number of log entries")},
   {"no-unlock",     opt_no_unlock, 0, N_("don't unlock the targets")},
   {"summarize",     opt_summarize, 0, N_("show a summary of the results")},
-  {"remove",         opt_remove, 0, N_("remove changelist association")},
+  {"remove",        opt_remove, 0, N_("remove an existing association")},
   {"changelist",    opt_changelist, 1,
                     N_("operate only on members of changelist ARG")},
   {"keep-changelists", opt_keep_changelists, 0,
@@ -445,6 +445,13 @@ const svn_opt_subcommand_desc2_t svn_cl_
      "       2. changelist --remove PATH...\n"),
     { 'q', 'R', opt_depth, opt_remove, opt_targets, opt_changelist} },
 
+  { "changepassword", svn_cl__changepassword, {"chpasswd"}, N_
+    ("Set (or unset) the master password/passphrase used to encrypt locally\n"
+     "cached authentication credentials.\n"
+     "usage: 1. changepassword NEWPASSWORD\n"
+     "       2. changepassword --remove\n"),
+    { 'q', 'R', opt_depth, opt_remove, opt_targets, opt_changelist} },
+
   { "checkout", svn_cl__checkout, {"co"}, N_
     ("Check out a working copy from a repository.\n"
      "usage: checkout URL[@REV]... [PATH]\n"