You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by rn...@apache.org on 2015/06/23 17:56:08 UTC

couch commit: updated refs/heads/master to 2c19dcd

Repository: couchdb-couch
Updated Branches:
  refs/heads/master 04312dc9a -> 2c19dcd0c


Configurable password scheme

This gives the administrator control over which algorithm is used to
hash passwords and a separate control over whether this happens on
successful authentication or only at password change time.

closes COUCHDB-2725


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/2c19dcd0
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/2c19dcd0
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/2c19dcd0

Branch: refs/heads/master
Commit: 2c19dcd0c528b8dccafe92803edc4dff88e40909
Parents: 04312dc
Author: Robert Newson <rn...@apache.org>
Authored: Tue Jun 16 17:17:44 2015 +0100
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Jun 23 14:08:22 2015 +0100

----------------------------------------------------------------------
 src/couch_httpd_auth.erl |  6 ++++--
 src/couch_passwords.erl  |  9 +++++++++
 src/couch_users_db.erl   | 25 ++++++++++++++++++++-----
 3 files changed, 33 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/2c19dcd0/src/couch_httpd_auth.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd_auth.erl b/src/couch_httpd_auth.erl
index 01453d8..0e78946 100644
--- a/src/couch_httpd_auth.erl
+++ b/src/couch_httpd_auth.erl
@@ -372,9 +372,11 @@ maybe_value(Key, Else, Fun) ->
 
 maybe_upgrade_password_hash(Req, UserName, Password, UserProps,
         AuthModule, AuthCtx) ->
+    Upgrade = config:get_boolean("couch_httpd_auth", "upgrade_password_on_auth", true),
     IsAdmin = lists:member(<<"_admin">>, couch_util:get_value(<<"roles">>, UserProps, [])),
-    case {IsAdmin, couch_util:get_value(<<"password_scheme">>, UserProps, <<"simple">>)} of
-    {false, <<"simple">>} ->
+    case {IsAdmin, Upgrade,
+         couch_util:get_value(<<"password_scheme">>, UserProps, <<"simple">>)} of
+    {false, true, <<"simple">>} ->
         UserProps2 = proplists:delete(<<"password_sha">>, UserProps),
         UserProps3 = [{<<"password">>, Password} | UserProps2],
         NewUserDoc = couch_doc:from_json_obj({UserProps3}),

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/2c19dcd0/src/couch_passwords.erl
----------------------------------------------------------------------
diff --git a/src/couch_passwords.erl b/src/couch_passwords.erl
index 2ab7cc3..b06a584 100644
--- a/src/couch_passwords.erl
+++ b/src/couch_passwords.erl
@@ -30,6 +30,15 @@ simple(Password, Salt) when is_binary(Password), is_binary(Salt) ->
 hash_admin_password(ClearPassword) when is_list(ClearPassword) ->
     hash_admin_password(?l2b(ClearPassword));
 hash_admin_password(ClearPassword) when is_binary(ClearPassword) ->
+    %% Support both schemes to smooth migration from legacy scheme
+    Scheme = config:get("couch_httpd_auth", "password_scheme", "pbkdf2"),
+    hash_admin_password(Scheme, ClearPassword).
+
+hash_admin_password("simple", ClearPassword) -> % deprecated
+    Salt = couch_uuids:random(),
+    Hash = crypto:sha(<<ClearPassword/binary, Salt/binary>>),
+    ?l2b("-hashed-" ++ couch_util:to_hex(Hash) ++ "," ++ ?b2l(Salt));
+hash_admin_password("pbkdf2", ClearPassword) ->
     Iterations = config:get("couch_httpd_auth", "iterations", "10000"),
     Salt = couch_uuids:random(),
     DerivedKey = couch_passwords:pbkdf2(couch_util:to_binary(ClearPassword),

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/2c19dcd0/src/couch_users_db.erl
----------------------------------------------------------------------
diff --git a/src/couch_users_db.erl b/src/couch_users_db.erl
index ade1955..6f7b9af 100644
--- a/src/couch_users_db.erl
+++ b/src/couch_users_db.erl
@@ -20,6 +20,8 @@
 -define(PASSWORD, <<"password">>).
 -define(DERIVED_KEY, <<"derived_key">>).
 -define(PASSWORD_SCHEME, <<"password_scheme">>).
+-define(SIMPLE, <<"simple">>).
+-define(PASSWORD_SHA, <<"password_sha">>).
 -define(PBKDF2, <<"pbkdf2">>).
 -define(ITERATIONS, <<"iterations">>).
 -define(SALT, <<"salt">>).
@@ -57,12 +59,22 @@ before_doc_update(Doc, #db{user_ctx = UserCtx} = Db) ->
 %    newDoc.salt = salt
 %    newDoc.password = null
 save_doc(#doc{body={Body}} = Doc) ->
-    case couch_util:get_value(?PASSWORD, Body) of
-    null -> % server admins don't have a user-db password entry
+    %% Support both schemes to smooth migration from legacy scheme
+    Scheme = config:get("couch_httpd_auth", "password_scheme", "pbkdf2"),
+    case {couch_util:get_value(?PASSWORD, Body), Scheme} of
+    {null, _} -> % server admins don't have a user-db password entry
         Doc;
-    undefined ->
+    {undefined, _} ->
         Doc;
-    ClearPassword ->
+    {ClearPassword, "simple"} -> % deprecated
+        Salt = couch_uuids:random(),
+        PasswordSha = couch_passwords:simple(ClearPassword, Salt),
+        Body0 = ?replace(Body, ?PASSWORD_SCHEME, ?SIMPLE),
+        Body1 = ?replace(Body0, ?SALT, Salt),
+        Body2 = ?replace(Body1, ?PASSWORD_SHA, PasswordSha),
+        Body3 = proplists:delete(?PASSWORD, Body2),
+        Doc#doc{body={Body3}};
+    {ClearPassword, "pbkdf2"} ->
         Iterations = list_to_integer(config:get("couch_httpd_auth", "iterations", "1000")),
         Salt = couch_uuids:random(),
         DerivedKey = couch_passwords:pbkdf2(ClearPassword, Salt, Iterations),
@@ -71,7 +83,10 @@ save_doc(#doc{body={Body}} = Doc) ->
         Body2 = ?replace(Body1, ?DERIVED_KEY, DerivedKey),
         Body3 = ?replace(Body2, ?SALT, Salt),
         Body4 = proplists:delete(?PASSWORD, Body3),
-        Doc#doc{body={Body4}}
+        Doc#doc{body={Body4}};
+    {_ClearPassword, Scheme} ->
+        couch_log:error("[couch_httpd_auth] password_scheme value of '~p' is invalid.", [Scheme]),
+        throw({forbidden, "Server cannot hash passwords at this time."})
     end.
 
 % If the doc is a design doc