aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel White <[email protected]>2014-10-20 08:43:40 +1100
committerDaniel White <[email protected]>2014-10-20 12:10:29 +1100
commit4130dc841ca68e16f5d97e89653fa49b16f4e793 (patch)
tree3d2adf834a58266f6a86eb9904463566ff0dbde5
parent7c4237e6d34b23020fca983731a3c7f07a10a8b5 (diff)
downloadotp-4130dc841ca68e16f5d97e89653fa49b16f4e793.tar.gz
otp-4130dc841ca68e16f5d97e89653fa49b16f4e793.tar.bz2
otp-4130dc841ca68e16f5d97e89653fa49b16f4e793.zip
eldap: Add support for modifying passwords
This implements the LDAP Password Modify Extended Operation (RFC 3062) with two new functions modify_password/3 and modify_password/4. The former is for directly setting passwords, and the latter is for users to change their own passwords. Since all three parameters are optional, I've opted to support this with the empty string rather than a property list. This seems consistent with other functions in the module (i.e. modify_dn/5).
-rw-r--r--lib/eldap/asn1/ELDAPv3.asn112
-rw-r--r--lib/eldap/doc/src/eldap.xml40
-rw-r--r--lib/eldap/src/eldap.erl77
-rw-r--r--lib/eldap/test/eldap_basic_SUITE.erl18
4 files changed, 147 insertions, 0 deletions
diff --git a/lib/eldap/asn1/ELDAPv3.asn1 b/lib/eldap/asn1/ELDAPv3.asn1
index 72b87d7221..3fe7e815cc 100644
--- a/lib/eldap/asn1/ELDAPv3.asn1
+++ b/lib/eldap/asn1/ELDAPv3.asn1
@@ -274,5 +274,17 @@ IntermediateResponse ::= [APPLICATION 25] SEQUENCE {
responseName [0] LDAPOID OPTIONAL,
responseValue [1] OCTET STRING OPTIONAL }
+-- Extended syntax for Password Modify (RFC 3062, Section 2)
+
+-- passwdModifyOID OBJECT IDENTIFIER ::= 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/lib/eldap/doc/src/eldap.xml b/lib/eldap/doc/src/eldap.xml
index dbd478fb17..d1a948437a 100644
--- a/lib/eldap/doc/src/eldap.xml
+++ b/lib/eldap/doc/src/eldap.xml
@@ -214,6 +214,46 @@ filter() See present/1, substrings/2,
</desc>
</func>
<func>
+ <name>modify_password(Handle, Dn, NewPasswd) -> ok | {ok, GenPasswd} | {error, Reason}</name>
+ <fsummary>Modify the password of a user.</fsummary>
+ <type>
+ <v>Dn = string()</v>
+ <v>NewPasswd = string()</v>
+ </type>
+ <desc>
+ <p>Modify the password of a user. See <seealso marker="#modify_password/4">modify_password/4</seealso>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>modify_password(Handle, Dn, NewPasswd, OldPasswd) -> ok | {ok, GenPasswd} | {error, Reason}</name>
+ <fsummary>Modify the password of a user.</fsummary>
+ <type>
+ <v>Dn = string()</v>
+ <v>NewPasswd = string()</v>
+ <v>OldPasswd = string()</v>
+ <v>GenPasswd = string()</v>
+ </type>
+ <desc>
+ <p>Modify the password of a user.</p>
+ <list type="bulleted">
+ <item>
+ <p><c>Dn</c>. The user to modify. Should be "" if the
+ modify request is for the user of the LDAP session.</p>
+ </item>
+ <item>
+ <p><c>NewPasswd</c>. The new password to set. Should be ""
+ if the server is to generate the password. In this case,
+ the result will be <c>{ok, GenPasswd}</c>.</p>
+ </item>
+ <item>
+ <p><c>OldPasswd</c>. Sometimes required by server policy
+ for a user to change their password. If not required, use
+ <seealso marker="#modify_password/3">modify_password/3</seealso>.</p>
+ </item>
+ </list>
+ </desc>
+ </func>
+ <func>
<name>modify_dn(Handle, Dn, NewRDN, DeleteOldRDN, NewSupDN) -> ok | {error, Reason}</name>
<fsummary>Modify the DN of an entry.</fsummary>
<type>
diff --git a/lib/eldap/src/eldap.erl b/lib/eldap/src/eldap.erl
index 416334e365..fd94cbfbfc 100644
--- a/lib/eldap/src/eldap.erl
+++ b/lib/eldap/src/eldap.erl
@@ -12,6 +12,7 @@
-vc('$Id$ ').
-export([open/1,open/2,simple_bind/3,controlling_process/2,
start_tls/2, start_tls/3,
+ modify_password/3, modify_password/4,
getopts/2,
baseObject/0,singleLevel/0,wholeSubtree/0,close/1,
equalityMatch/2,greaterOrEqual/2,lessOrEqual/2,
@@ -93,6 +94,23 @@ start_tls(Handle, TlsOptions, Timeout) ->
recv(Handle).
%%% --------------------------------------------------------------------
+%%% Modify the password of a user.
+%%%
+%%% Dn - Name of the entry to modify. If empty, the session user.
+%%% NewPasswd - New password. If empty, the server returns a new password.
+%%% OldPasswd - Original password for server verification, may be empty.
+%%%
+%%% Returns: ok | {ok, GenPasswd} | {error, term()}
+%%% --------------------------------------------------------------------
+modify_password(Handle, Dn, NewPasswd) ->
+ modify_password(Handle, Dn, NewPasswd, []).
+
+modify_password(Handle, Dn, NewPasswd, OldPasswd)
+ when is_pid(Handle), is_list(Dn), is_list(NewPasswd), is_list(OldPasswd) ->
+ send(Handle, {passwd_modify,optional(Dn),optional(NewPasswd),optional(OldPasswd)}),
+ recv(Handle).
+
+%%% --------------------------------------------------------------------
%%% Ask for option values on the socket.
%%% Warning: This is an undocumented function for testing purposes only.
%%% Use at own risk...
@@ -483,6 +501,11 @@ loop(Cpid, Data) ->
send(From,Res),
?MODULE:loop(Cpid, NewData);
+ {From, {passwd_modify,Dn,NewPasswd,OldPasswd}} ->
+ {Res,NewData} = do_passwd_modify(Data, Dn, NewPasswd, OldPasswd),
+ send(From, Res),
+ ?MODULE:loop(Cpid, NewData);
+
{_From, close} ->
unlink(Cpid),
exit(closed);
@@ -773,6 +796,60 @@ do_modify_0(Data, Obj, Mod) ->
check_reply(Data#eldap{id = Id}, Resp, modifyResponse).
%%% --------------------------------------------------------------------
+%%% PasswdModifyRequest
+%%% --------------------------------------------------------------------
+
+-define(PASSWD_MODIFY_OID, "1.3.6.1.4.1.4203.1.11.1").
+
+do_passwd_modify(Data, Dn, NewPasswd, OldPasswd) ->
+ case catch do_passwd_modify_0(Data, Dn, NewPasswd, OldPasswd) of
+ {error,Emsg} -> {ldap_closed_p(Data, Emsg),Data};
+ {'EXIT',Error} -> {ldap_closed_p(Data, Error),Data};
+ {ok,NewData} -> {ok,NewData};
+ {ok,Passwd,NewData} -> {{ok, Passwd},NewData};
+ Else -> {ldap_closed_p(Data, Else),Data}
+ end.
+
+do_passwd_modify_0(Data, Dn, NewPasswd, OldPasswd) ->
+ Req = #'PasswdModifyRequestValue'{userIdentity = Dn,
+ oldPasswd = OldPasswd,
+ newPasswd = NewPasswd},
+ log2(Data, "modify password request = ~p~n", [Req]),
+ {ok, Bytes} = 'ELDAPv3':encode('PasswdModifyRequestValue', Req),
+ ExtReq = #'ExtendedRequest'{requestName = ?PASSWD_MODIFY_OID,
+ requestValue = Bytes},
+ Id = bump_id(Data),
+ log2(Data, "extended request = ~p~n", [ExtReq]),
+ Reply = request(Data#eldap.fd, Data, Id, {extendedReq, ExtReq}),
+ log2(Data, "modify password reply = ~p~n", [Reply]),
+ exec_passwd_modify_reply(Data#eldap{id = Id}, Reply).
+
+exec_passwd_modify_reply(Data, {ok,Msg}) when
+ Msg#'LDAPMessage'.messageID == Data#eldap.id ->
+ case Msg#'LDAPMessage'.protocolOp of
+ {extendedResp, Result} ->
+ case Result#'ExtendedResponse'.resultCode of
+ success ->
+ case Result#'ExtendedResponse'.responseValue of
+ asn1_NOVALUE ->
+ {ok, Data};
+ Value ->
+ case 'ELDAPv3':decode('PasswdModifyResponseValue', Value) of
+ {ok,#'PasswdModifyResponseValue'{genPasswd = Passwd}} ->
+ {ok, Passwd, Data};
+ Error ->
+ throw(Error)
+ end
+ end;
+ Error ->
+ {error, {response,Error}}
+ end;
+ Other -> {error, Other}
+ end;
+exec_passwd_modify_reply(_, Error) ->
+ {error, Error}.
+
+%%% --------------------------------------------------------------------
%%% modifyDNRequest
%%% --------------------------------------------------------------------
diff --git a/lib/eldap/test/eldap_basic_SUITE.erl b/lib/eldap/test/eldap_basic_SUITE.erl
index 6c3d303da0..68a7b0c811 100644
--- a/lib/eldap/test/eldap_basic_SUITE.erl
+++ b/lib/eldap/test/eldap_basic_SUITE.erl
@@ -198,6 +198,7 @@ do_api_checks(H, Config) ->
chk_add(H, BasePath),
{ok,FB} = chk_search(H, BasePath),
chk_modify(H, FB),
+ chk_modify_password(H, FB),
chk_delete(H, BasePath),
chk_modify_dn(H, FB).
@@ -242,6 +243,23 @@ chk_modify(H, FB) ->
%% DELETE ATTR
ok = eldap:modify(H, FB, [eldap:mod_delete("telephoneNumber", [])]).
+chk_modify_password(H, FB) ->
+ %% Change password, and ensure we can bind with it.
+ ok = eldap:simple_bind(H, "cn=Manager,dc=ericsson,dc=se", "hejsan"),
+ ok = eldap:modify_password(H, FB, "example"),
+ ok = eldap:simple_bind(H, FB, "example"),
+ %% Change password to a server generated value.
+ ok = eldap:simple_bind(H, "cn=Manager,dc=ericsson,dc=se", "hejsan"),
+ {ok, Passwd} = eldap:modify_password(H, FB, []),
+ ok = eldap:simple_bind(H, FB, Passwd),
+ %% Change own password to server generated value.
+ {ok, NewPasswd} = eldap:modify_password(H, [], [], Passwd),
+ ok = eldap:simple_bind(H, FB, NewPasswd),
+ %% Change own password to explicit value.
+ ok = eldap:modify_password(H, [], "example", NewPasswd),
+ ok = eldap:simple_bind(H, FB, "example"),
+ %% Restore original binding.
+ ok = eldap:simple_bind(H, "cn=Manager,dc=ericsson,dc=se", "hejsan").
chk_delete(H, BasePath) ->
{error, entryAlreadyExists} = eldap:add(H, "cn=Jonas Jonsson," ++ BasePath,