diff --git a/src/ejabberd_auth_ldap.erl b/src/ejabberd_auth_ldap.erl index d76fb2ebc..065fde477 100644 --- a/src/ejabberd_auth_ldap.erl +++ b/src/ejabberd_auth_ldap.erl @@ -182,13 +182,20 @@ check_password(User, Server, Password) -> check_password(User, Server, Password, _Digest, _DigestGen) -> check_password(User, Server, Password). -%% @spec (User, Server, Password) -> {error, not_allowed} +%% @spec (User, Server, Password) -> {error, Reason} | ok %% User = string() %% Server = string() %% Password = string() +%% Reason = term() -set_password(_User, _Server, _Password) -> - {error, not_allowed}. +set_password(User, Server, Password) -> + {ok, State} = eldap_utils:get_state(Server, ?MODULE), + case find_user_dn(User, State) of + false -> + {error, user_not_found}; + DN -> + eldap_pool:modify_passwd(State#state.eldap_id, DN, Password) + end. %% @spec (User, Server, Password) -> {error, not_allowed} %% User = string() diff --git a/src/eldap/ELDAPv3.asn b/src/eldap/ELDAPv3.asn index 0cfac48c3..1fec35cd8 100644 --- a/src/eldap/ELDAPv3.asn +++ b/src/eldap/ELDAPv3.asn @@ -286,6 +286,16 @@ ExtendedResponse ::= [APPLICATION 24] SEQUENCE { responseName [10] LDAPOID OPTIONAL, response [11] OCTET STRING OPTIONAL } +passwdModifyOID LDAPOID ::= "1.3.6.1.4.1.4203.1.11.1" + +PasswdModifyRequestValue ::= SEQUENCE { + userIdentity [0] OCTET STRING OPTIONAL, + oldPasswd [1] OCTET STRING OPTIONAL, + newPasswd [2] OCTET STRING OPTIONAL } + +PasswdModifyResponseValue ::= SEQUENCE { + genPasswd [0] OCTET STRING OPTIONAL } + END diff --git a/src/eldap/eldap.erl b/src/eldap/eldap.erl index 1c7331768..5bc0c425a 100644 --- a/src/eldap/eldap.erl +++ b/src/eldap/eldap.erl @@ -33,9 +33,11 @@ %%% Modified by Alexey Shchepin -%%% Modified by Evgeniy Khramtsov +%%% Modified by Evgeniy Khramtsov %%% Implemented queue for bind() requests to prevent pending binds. %%% Implemented extensibleMatch/2 function. +%%% Implemented LDAP Extended Operations (currently only Password Modify +%%% is supported - RFC 3062). %%% Modified by Christophe Romain %%% Improve error case handling @@ -74,7 +76,7 @@ equalityMatch/2,greaterOrEqual/2,lessOrEqual/2, approxMatch/2,search/2,substrings/2,present/1,extensibleMatch/2, 'and'/1,'or'/1,'not'/1,modify/3, mod_add/2, mod_delete/2, - mod_replace/2, add/3, delete/2, modify_dn/5, bind/3]). + mod_replace/2, add/3, delete/2, modify_dn/5, modify_passwd/3, bind/3]). -export([get_status/1]). %% gen_fsm callbacks @@ -240,6 +242,10 @@ modify_dn(Handle, Entry, NewRDN, DelOldRDN, NewSup) {modify_dn, Entry, NewRDN, bool_p(DelOldRDN), optional(NewSup)}, ?CALL_TIMEOUT). +modify_passwd(Handle, DN, Passwd) when is_list(DN), is_list(Passwd) -> + Handle1 = get_handle(Handle), + gen_fsm:sync_send_event( + Handle1, {modify_passwd, DN, Passwd}, ?CALL_TIMEOUT). %%% -------------------------------------------------------------------- %%% Bind. @@ -695,6 +701,16 @@ gen_req({modify_dn, Entry, NewRDN, DelOldRDN, NewSup}) -> deleteoldrdn = DelOldRDN, newSuperior = NewSup}}; +gen_req({modify_passwd, DN, Passwd}) -> + {ok, ReqVal} = asn1rt:encode( + 'ELDAPv3', 'PasswdModifyRequestValue', + #'PasswdModifyRequestValue'{ + userIdentity = DN, + newPasswd = Passwd}), + {extendedReq, + #'ExtendedRequest'{requestName = ?passwdModifyOID, + requestValue = list_to_binary(ReqVal)}}; + gen_req({bind, RootDN, Passwd}) -> {bindRequest, #'BindRequest'{version = ?LDAP_VERSION, @@ -769,6 +785,11 @@ recvd_packet(Pkt, S) -> cancel_timer(Timer), Reply = check_bind_reply(Result, From), {reply, Reply, From, S#eldap{dict = New_dict}}; + {extendedReq, {extendedResp, Result}} -> + New_dict = dict:erase(Id, Dict), + cancel_timer(Timer), + Reply = check_extended_reply(Result, From), + {reply, Reply, From, S#eldap{dict = New_dict}}; {OtherName, OtherResult} -> New_dict = dict:erase(Id, Dict), cancel_timer(Timer), @@ -793,6 +814,15 @@ check_bind_reply(#'BindResponse'{resultCode = Reason}, _From) -> check_bind_reply(Other, _From) -> {error, Other}. +%% TODO: process reply depending on requestName: +%% this requires BER-decoding of #'ExtendedResponse'.response +check_extended_reply(#'ExtendedResponse'{resultCode = success}, _From) -> + ok; +check_extended_reply(#'ExtendedResponse'{resultCode = Reason}, _From) -> + {error, Reason}; +check_extended_reply(Other, _From) -> + {error, Other}. + get_op_rec(Id, Dict) -> case dict:find(Id, Dict) of {ok, [{Timer, _Command, From, Name}|Res]} -> diff --git a/src/eldap/eldap_pool.erl b/src/eldap/eldap_pool.erl index f6cb721d1..2331b2c05 100644 --- a/src/eldap/eldap_pool.erl +++ b/src/eldap/eldap_pool.erl @@ -31,7 +31,8 @@ -export([ start_link/7, bind/3, - search/2 + search/2, + modify_passwd/3 ]). -include("ejabberd.hrl"). @@ -45,6 +46,9 @@ bind(PoolName, DN, Passwd) -> search(PoolName, Opts) -> do_request(PoolName, {search, [Opts]}). +modify_passwd(PoolName, DN, Passwd) -> + do_request(PoolName, {modify_passwd, [DN, Passwd]}). + start_link(Name, Hosts, Backups, Port, Rootdn, Passwd, Encrypt) -> PoolName = make_id(Name), pg2:create(PoolName),