Permalink
Browse files

Make password hashing synchronous when using the /_config/admins API.

This should account for many intermittent JavaScript test suite errors.

The patch retains hashing admins on couch_server start to account for
users editing their .ini files directly.

Knowledge about password hash prefixes and the password hashing itself
has been moved to couch_passwords.

Thanks to Dale Harvey and Robert Newson for helping me to hunt this down
and shaping and reviewing the patch.
  • Loading branch information...
1 parent 1ffff82 commit 08071a80e0dc78b744cf56179c0aaac0a4390fde @janl janl committed Apr 23, 2012
Showing with 35 additions and 14 deletions.
  1. +6 −1 src/couchdb/couch_httpd_misc_handlers.erl
  2. +25 −0 src/couchdb/couch_passwords.erl
  3. +4 −13 src/couchdb/couch_server.erl
View
7 src/couchdb/couch_httpd_misc_handlers.erl
@@ -212,7 +212,12 @@ handle_config_req(Req) ->
% PUT /_config/Section/Key
% "value"
handle_approved_config_req(#httpd{method='PUT', path_parts=[_, Section, Key]}=Req, Persist) ->
- Value = couch_httpd:json_body(Req),
+ Value = case Section of
+ <<"admins">> ->
+ couch_passwords:hash_admin_password(couch_httpd:json_body(Req));
+ _ ->
+ couch_httpd:json_body(Req)
+ end,
OldValue = couch_config:get(Section, Key, ""),
case couch_config:set(Section, Key, ?b2l(Value), Persist) of
ok ->
View
25 src/couchdb/couch_passwords.erl
@@ -13,6 +13,8 @@
-module(couch_passwords).
-export([simple/2, pbkdf2/3, pbkdf2/4, verify/2]).
+-export([hash_admin_password/1, get_unhashed_admins/0]).
+
-include("couch_db.hrl").
-define(MAX_DERIVED_KEY_LENGTH, (1 bsl 32 - 1)).
@@ -23,6 +25,29 @@
simple(Password, Salt) ->
?l2b(couch_util:to_hex(crypto:sha(<<Password/binary, Salt/binary>>))).
+%% CouchDB utility functions
+-spec hash_admin_password(binary()) -> binary().
+hash_admin_password(ClearPassword) ->
+ Iterations = couch_config:get("couch_httpd_auth", "iterations", "10000"),
+ Salt = couch_uuids:random(),
+ DerivedKey = couch_passwords:pbkdf2(?b2l(ClearPassword), Salt,
+ list_to_integer(Iterations)),
+ ?l2b("-pbkdf2-" ++ ?b2l(DerivedKey) ++ ","
+ ++ ?b2l(Salt) ++ ","
+ ++ Iterations).
+
+-spec get_unhashed_admins() -> list().
+get_unhashed_admins() ->
+ lists:filter(
+ fun({_User, "-hashed-" ++ _}) ->
+ false; % already hashed
+ ({_User, "-pbkdf2-" ++ _}) ->
+ false; % already hashed
+ ({_User, _ClearPassword}) ->
+ true
+ end,
+ couch_config:get("admins")).
+
%% Current scheme, much stronger.
-spec pbkdf2(binary(), binary(), integer()) -> string().
pbkdf2(Password, Salt, Iterations) ->
View
17 src/couchdb/couch_server.erl
@@ -129,20 +129,11 @@ hash_admin_passwords() ->
hash_admin_passwords(true).
hash_admin_passwords(Persist) ->
- Iterations = couch_config:get("couch_httpd_auth", "iterations", "10000"),
lists:foreach(
- fun({_User, "-hashed-" ++ _}) ->
- ok; % already hashed
- ({_User, "-pbkdf2-" ++ _}) ->
- ok; % already hashed
- ({User, ClearPassword}) ->
- Salt = couch_uuids:random(),
- DerivedKey = couch_passwords:pbkdf2(ClearPassword, Salt,
- list_to_integer(Iterations)),
- couch_config:set("admins",
- User, "-pbkdf2-" ++ ?b2l(DerivedKey) ++ "," ++ ?b2l(Salt) ++
- "," ++ Iterations, Persist)
- end, couch_config:get("admins")).
+ fun({User, ClearPassword}) ->
+ HashedPassword = couch_passwords:hash_admin_password(ClearPassword),
+ couch_config:set("admins", User, ?b2l(HashedPassword), Persist)
+ end, couch_passwords:get_unhashed_admins()).
init([]) ->
% read config and register for configuration changes

0 comments on commit 08071a8

Please sign in to comment.