diff options
Diffstat (limited to 'src/ejabberd_auth_riak.erl')
-rw-r--r-- | src/ejabberd_auth_riak.erl | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/src/ejabberd_auth_riak.erl b/src/ejabberd_auth_riak.erl new file mode 100644 index 000000000..870aa4890 --- /dev/null +++ b/src/ejabberd_auth_riak.erl @@ -0,0 +1,285 @@ +%%%---------------------------------------------------------------------- +%%% File : ejabberd_auth_riak.erl +%%% Author : Evgeniy Khramtsov <ekhramtsov@process-one.net> +%%% Purpose : Authentification via Riak +%%% Created : 12 Nov 2012 by Evgeniy Khramtsov <ekhramtsov@process-one.net> +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2012 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License +%%% along with this program; if not, write to the Free Software +%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +%%% 02111-1307 USA +%%% +%%%---------------------------------------------------------------------- + +-module(ejabberd_auth_riak). + +-author('alexey@process-one.net'). + +-behaviour(ejabberd_auth). + +%% External exports +-export([start/1, set_password/3, check_password/3, + check_password/5, try_register/3, + dirty_get_registered_users/0, get_vh_registered_users/1, + get_vh_registered_users/2, + get_vh_registered_users_number/1, + get_vh_registered_users_number/2, get_password/2, + get_password_s/2, is_user_exists/2, remove_user/2, + remove_user/3, store_type/0, export/1, + plain_password_required/0]). + +-include("ejabberd.hrl"). + +-record(passwd, {us = {<<"">>, <<"">>} :: {binary(), binary()} | '$1', + password = <<"">> :: binary() | scram() | '_'}). + +-define(SALT_LENGTH, 16). + +start(_Host) -> + ok. + +plain_password_required() -> + case is_scrammed() of + false -> false; + true -> true + end. + +store_type() -> + case is_scrammed() of + false -> plain; %% allows: PLAIN DIGEST-MD5 SCRAM + true -> scram %% allows: PLAIN SCRAM + end. + +check_password(User, Server, Password) -> + LUser = jlib:nodeprep(User), + LServer = jlib:nameprep(Server), + case ejabberd_riak:get(passwd, {LUser, LServer}) of + {ok, #passwd{password = Password}} when is_binary(Password) -> + Password /= <<"">>; + {ok, #passwd{password = Scram}} when is_record(Scram, scram) -> + is_password_scram_valid(Password, Scram); + _ -> + false + end. + +check_password(User, Server, Password, Digest, + DigestGen) -> + LUser = jlib:nodeprep(User), + LServer = jlib:nameprep(Server), + case ejabberd_riak:get(passwd, {LUser, LServer}) of + {ok, #passwd{password = Passwd}} when is_binary(Passwd) -> + DigRes = if Digest /= <<"">> -> + Digest == DigestGen(Passwd); + true -> false + end, + if DigRes -> true; + true -> (Passwd == Password) and (Password /= <<"">>) + end; + {ok, #passwd{password = Scram}} + when is_record(Scram, scram) -> + Passwd = jlib:decode_base64(Scram#scram.storedkey), + DigRes = if Digest /= <<"">> -> + Digest == DigestGen(Passwd); + true -> false + end, + if DigRes -> true; + true -> (Passwd == Password) and (Password /= <<"">>) + end; + _ -> false + end. + +set_password(User, Server, Password) -> + LUser = jlib:nodeprep(User), + LServer = jlib:nameprep(Server), + US = {LUser, LServer}, + if (LUser == error) or (LServer == error) -> + {error, invalid_jid}; + true -> + Password2 = case is_scrammed() and is_binary(Password) + of + true -> password_to_scram(Password); + false -> Password + end, + ok = ejabberd_riak:put(#passwd{us = US, password = Password2}, + [{'2i', [{<<"host">>, LServer}]}]) + end. + +try_register(User, Server, PasswordList) -> + LUser = jlib:nodeprep(User), + LServer = jlib:nameprep(Server), + Password = iolist_to_binary(PasswordList), + US = {LUser, LServer}, + if (LUser == error) or (LServer == error) -> + {error, invalid_jid}; + true -> + case ejabberd_riak:get(passwd, US) of + {error, notfound} -> + Password2 = case is_scrammed() and + is_binary(Password) + of + true -> password_to_scram(Password); + false -> Password + end, + {atomic, ejabberd_riak:put( + #passwd{us = US, + password = Password2}, + [{'2i', [{<<"host">>, LServer}]}])}; + {ok, _} -> + exists; + Err -> + {atomic, Err} + end + end. + +dirty_get_registered_users() -> + lists:flatmap( + fun(Server) -> + get_vh_registered_users(Server) + end, ejabberd_config:get_vh_by_auth_method(riak)). + +get_vh_registered_users(Server) -> + LServer = jlib:nameprep(Server), + case ejabberd_riak:get_keys_by_index(passwd, <<"host">>, LServer) of + {ok, Users} -> + Users; + _ -> + [] + end. + +get_vh_registered_users(Server, _) -> + get_vh_registered_users(Server). + +get_vh_registered_users_number(Server) -> + LServer = jlib:nameprep(Server), + case ejabberd_riak:count_by_index(passwd, <<"host">>, LServer) of + {ok, N} -> + N; + _ -> + 0 + end. + +get_vh_registered_users_number(Server, _) -> + get_vh_registered_users_number(Server). + +get_password(User, Server) -> + LUser = jlib:nodeprep(User), + LServer = jlib:nameprep(Server), + case ejabberd_riak:get(passwd, {LUser, LServer}) of + {ok, #passwd{password = Password}} + when is_binary(Password) -> + Password; + {ok, #passwd{password = Scram}} + when is_record(Scram, scram) -> + {jlib:decode_base64(Scram#scram.storedkey), + jlib:decode_base64(Scram#scram.serverkey), + jlib:decode_base64(Scram#scram.salt), + Scram#scram.iterationcount}; + _ -> false + end. + +get_password_s(User, Server) -> + LUser = jlib:nodeprep(User), + LServer = jlib:nameprep(Server), + case ejabberd_riak:get(passwd, {LUser, LServer}) of + {ok, #passwd{password = Password}} + when is_binary(Password) -> + Password; + {ok, #passwd{password = Scram}} + when is_record(Scram, scram) -> + <<"">>; + _ -> <<"">> + end. + +is_user_exists(User, Server) -> + LUser = jlib:nodeprep(User), + LServer = jlib:nameprep(Server), + case ejabberd_riak:get(passwd, {LUser, LServer}) of + {error, notfound} -> false; + {ok, _} -> true; + Err -> Err + end. + +remove_user(User, Server) -> + LUser = jlib:nodeprep(User), + LServer = jlib:nameprep(Server), + ejabberd_riak:delete(passwd, {LUser, LServer}), + ok. + +remove_user(User, Server, Password) -> + LUser = jlib:nodeprep(User), + LServer = jlib:nameprep(Server), + case ejabberd_riak:get(passwd, {LUser, LServer}) of + {ok, #passwd{password = Password}} + when is_binary(Password) -> + ejabberd_riak:delete(passwd, {LUser, LServer}), + ok; + {ok, #passwd{password = Scram}} + when is_record(Scram, scram) -> + case is_password_scram_valid(Password, Scram) of + true -> + ejabberd_riak:delete(passwd, {LUser, LServer}), + ok; + false -> not_allowed + end; + _ -> not_exists + end. + +%%% +%%% SCRAM +%%% + +is_scrammed() -> + scram == + ejabberd_config:get_local_option({auth_password_format, ?MYNAME}, + fun(V) -> V end). + +password_to_scram(Password) -> + password_to_scram(Password, + ?SCRAM_DEFAULT_ITERATION_COUNT). + +password_to_scram(Password, IterationCount) -> + Salt = crypto:rand_bytes(?SALT_LENGTH), + SaltedPassword = scram:salted_password(Password, Salt, + IterationCount), + StoredKey = + scram:stored_key(scram:client_key(SaltedPassword)), + ServerKey = scram:server_key(SaltedPassword), + #scram{storedkey = jlib:encode_base64(StoredKey), + serverkey = jlib:encode_base64(ServerKey), + salt = jlib:encode_base64(Salt), + iterationcount = IterationCount}. + +is_password_scram_valid(Password, Scram) -> + IterationCount = Scram#scram.iterationcount, + Salt = jlib:decode_base64(Scram#scram.salt), + SaltedPassword = scram:salted_password(Password, Salt, + IterationCount), + StoredKey = + scram:stored_key(scram:client_key(SaltedPassword)), + jlib:decode_base64(Scram#scram.storedkey) == StoredKey. + +export(_Server) -> + [{passwd, + fun(Host, #passwd{us = {LUser, LServer}, password = Password}) + when LServer == Host -> + Username = ejabberd_odbc:escape(LUser), + Pass = ejabberd_odbc:escape(Password), + [[<<"delete from users where username='">>, Username, <<"';">>], + [<<"insert into users(username, password) " + "values ('">>, Username, <<"', '">>, Pass, <<"');">>]]; + (_Host, _R) -> + [] + end}]. |