summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Newson <rnewson@apache.org>2014-04-06 18:31:15 +0100
committerRobert Newson <rnewson@apache.org>2014-04-06 22:09:54 +0100
commitdbe769c604e5f1e000594336f8c4546bf9e3fd5a (patch)
tree093c6ad90733e6629b27cc9171b7879cf53b86ff
parent9f6a9190f04a23690277888b5ae2413f7cef7a96 (diff)
downloadcouchdb-2221-bug-validate-auth-params.tar.gz
Verify that auth-related properties are well-formed2221-bug-validate-auth-params
Passing unexpected values to auth fields can result in server issues. Notably, setting "iterations" to a string will cause an infinite loop as the comparison 'when Iteration > Iterations' will never evaluate to true. The latest validate_doc_update prevents user docs with this problem and administrators can deploy that check themselves (and only administrators can edit design documents). A server administrator can also insist on lower and upper bounds for iteration count to reject weakly protected passwords and resource-hungry passwords respectively. COUCHDB-2221
-rw-r--r--etc/couchdb/default.ini.tpl.in2
-rw-r--r--share/doc/src/config/auth.rst24
-rw-r--r--src/couchdb/couch_httpd_auth.erl17
-rw-r--r--src/couchdb/couch_passwords.erl15
4 files changed, 54 insertions, 4 deletions
diff --git a/etc/couchdb/default.ini.tpl.in b/etc/couchdb/default.ini.tpl.in
index a0dd7db20..934c6cd44 100644
--- a/etc/couchdb/default.ini.tpl.in
+++ b/etc/couchdb/default.ini.tpl.in
@@ -72,6 +72,8 @@ timeout = 600 ; number of seconds before automatic logout
auth_cache_size = 50 ; size is number of cache entries
allow_persistent_cookies = false ; set to true to allow persistent cookies
iterations = 10 ; iterations for password hashing
+; min_iterations = 1
+; max_iterations = 1000000000
; comma-separated list of public fields, 404 if empty
; public_fields =
diff --git a/share/doc/src/config/auth.rst b/share/doc/src/config/auth.rst
index 41272887c..831114094 100644
--- a/share/doc/src/config/auth.rst
+++ b/share/doc/src/config/auth.rst
@@ -166,6 +166,30 @@ Authentication Configuration
[couch_httpd_auth]
iterations = 10000
+ .. config:option:: min_iterations :: Minimum PBKDF2 iterations count
+
+ .. versionadded:: 1.6
+
+ The minimum number of iterations allowed for passwords hashed by
+ the PBKDF2 algorithm. Any user with fewer iterations is forbidden.
+
+ ::
+
+ [couch_httpd_auth]
+ min_iterations = 100
+
+ .. config:option:: max_iterations :: Maximum PBKDF2 iterations count
+
+ .. versionadded:: 1.6
+
+ The maximum number of iterations allowed for passwords hashed by
+ the PBKDF2 algorithm. Any user with greater iterations is forbidden.
+
+ ::
+
+ [couch_httpd_auth]
+ max_iterations = 100000
+
.. config:option:: proxy_use_secret :: Force proxy auth use secret token
diff --git a/src/couchdb/couch_httpd_auth.erl b/src/couchdb/couch_httpd_auth.erl
index 08841fb67..6888f0691 100644
--- a/src/couchdb/couch_httpd_auth.erl
+++ b/src/couchdb/couch_httpd_auth.erl
@@ -368,11 +368,28 @@ authenticate(Pass, UserProps) ->
couch_util:get_value(<<"password_sha">>, UserProps, nil)};
<<"pbkdf2">> ->
Iterations = couch_util:get_value(<<"iterations">>, UserProps, 10000),
+ verify_iterations(Iterations),
{couch_passwords:pbkdf2(Pass, UserSalt, Iterations),
couch_util:get_value(<<"derived_key">>, UserProps, nil)}
end,
couch_passwords:verify(PasswordHash, ExpectedHash).
+verify_iterations(Iterations) when is_integer(Iterations) ->
+ Min = list_to_integer(couch_config:get("couch_httpd_auth", "min_iterations", "1")),
+ Max = list_to_integer(couch_config:get("couch_httpd_auth", "max_iterations", "1000000000")),
+ case Iterations < Min of
+ true ->
+ throw({forbidden, <<"Iteration count is too low for this server">>});
+ false ->
+ ok
+ end,
+ case Iterations > Max of
+ true ->
+ throw({forbidden, <<"Iteration count is too high for this server">>});
+ false ->
+ ok
+ end.
+
auth_name(String) when is_list(String) ->
[_,_,_,_,_,Name|_] = re:split(String, "[\\W_]", [{return, list}]),
?l2b(Name).
diff --git a/src/couchdb/couch_passwords.erl b/src/couchdb/couch_passwords.erl
index d9e6836db..bbf6d9ac3 100644
--- a/src/couchdb/couch_passwords.erl
+++ b/src/couchdb/couch_passwords.erl
@@ -22,12 +22,12 @@
%% legacy scheme, not used for new passwords.
-spec simple(binary(), binary()) -> binary().
-simple(Password, Salt) ->
+simple(Password, Salt) when is_binary(Password), is_binary(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) ->
+hash_admin_password(ClearPassword) when is_binary(ClearPassword) ->
Iterations = couch_config:get("couch_httpd_auth", "iterations", "10000"),
Salt = couch_uuids:random(),
DerivedKey = couch_passwords:pbkdf2(couch_util:to_binary(ClearPassword),
@@ -50,7 +50,10 @@ get_unhashed_admins() ->
%% Current scheme, much stronger.
-spec pbkdf2(binary(), binary(), integer()) -> binary().
-pbkdf2(Password, Salt, Iterations) ->
+pbkdf2(Password, Salt, Iterations) when is_binary(Password),
+ is_binary(Salt),
+ is_integer(Iterations),
+ Iterations > 0 ->
{ok, Result} = pbkdf2(Password, Salt, Iterations, ?SHA1_OUTPUT_LENGTH),
Result.
@@ -59,7 +62,11 @@ pbkdf2(Password, Salt, Iterations) ->
pbkdf2(_Password, _Salt, _Iterations, DerivedLength)
when DerivedLength > ?MAX_DERIVED_KEY_LENGTH ->
{error, derived_key_too_long};
-pbkdf2(Password, Salt, Iterations, DerivedLength) ->
+pbkdf2(Password, Salt, Iterations, DerivedLength) when is_binary(Password),
+ is_binary(Salt),
+ is_integer(Iterations),
+ Iterations > 0,
+ is_integer(DerivedLength) ->
L = ceiling(DerivedLength / ?SHA1_OUTPUT_LENGTH),
<<Bin:DerivedLength/binary,_/binary>> =
iolist_to_binary(pbkdf2(Password, Salt, Iterations, L, 1, [])),