Skip to content

Commit 917d48f

Browse files
committed
Use SASL PLAIN authzid as client identity if auth module permits it
This allows the authentication modules to perform SASL proxy authentication. It puts the onus on them to authorize the authcid to masquerade as the authzid. Doesn't currently implement such functionality in existing auth modules, since they cannot currently codify a relationship between the two identities. Does not permit the authzid to use a domain differently from the one of the connection. Note: digest might not work, but I have no interest in it, being deprecated.
1 parent d981470 commit 917d48f

16 files changed

Lines changed: 306 additions & 261 deletions

src/cyrsasl.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ register_mechanism(Mechanism, Module, PasswordType) ->
128128
%% end.
129129

130130
check_credentials(_State, Props) ->
131-
User = proplists:get_value(username, Props, <<>>),
131+
User = proplists:get_value(authzid, Props, <<>>),
132132
case jlib:nodeprep(User) of
133133
error -> {error, <<"not-authorized">>};
134134
<<"">> -> {error, <<"not-authorized">>};

src/cyrsasl_digest.erl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
username = <<"">> :: binary(),
4848
authzid = <<"">> :: binary(),
4949
get_password = fun(_) -> {false, <<>>} end :: get_password_fun(),
50-
check_password = fun(_, _, _, _) -> false end :: check_password_fun(),
50+
check_password = fun(_, _, _, _, _) -> false end :: check_password_fun(),
5151
auth_module :: atom(),
5252
host = <<"">> :: binary(),
5353
hostfqdn = <<"">> :: binary()}).
@@ -95,7 +95,7 @@ mech_step(#state{step = 3, nonce = Nonce} = State,
9595
case (State#state.get_password)(UserName) of
9696
{false, _} -> {error, <<"not-authorized">>, UserName};
9797
{Passwd, AuthModule} ->
98-
case (State#state.check_password)(UserName, <<"">>,
98+
case (State#state.check_password)(UserName, UserName, <<"">>,
9999
proplists:get_value(<<"response">>, KeyVals, <<>>),
100100
fun (PW) ->
101101
response(KeyVals,
@@ -123,7 +123,11 @@ mech_step(#state{step = 5, auth_module = AuthModule,
123123
username = UserName, authzid = AuthzId},
124124
<<"">>) ->
125125
{ok,
126-
[{username, UserName}, {authzid, AuthzId},
126+
[{username, UserName}, {authzid, case AuthzId of
127+
<<"">> -> UserName;
128+
_ -> AuthzId
129+
end
130+
},
127131
{auth_module, AuthModule}]};
128132
mech_step(A, B) ->
129133
?DEBUG("SASL DIGEST: A ~p B ~p", [A, B]),

src/cyrsasl_plain.erl

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ mech_new(_Host, _GetPassword, CheckPassword, _CheckPasswordDigest) ->
4545
mech_step(State, ClientIn) ->
4646
case prepare(ClientIn) of
4747
[AuthzId, User, Password] ->
48-
case (State#state.check_password)(User, Password) of
48+
case (State#state.check_password)(User, AuthzId, Password) of
4949
{true, AuthModule} ->
5050
{ok,
5151
[{username, User}, {authzid, AuthzId},
@@ -60,12 +60,17 @@ prepare(ClientIn) ->
6060
[<<"">>, UserMaybeDomain, Password] ->
6161
case parse_domain(UserMaybeDomain) of
6262
%% <NUL>login@domain<NUL>pwd
63-
[User, _Domain] -> [UserMaybeDomain, User, Password];
63+
[User, _Domain] -> [User, User, Password];
6464
%% <NUL>login<NUL>pwd
65-
[User] -> [<<"">>, User, Password]
65+
[User] -> [User, User, Password]
6666
end;
67-
%% login@domain<NUL>login<NUL>pwd
68-
[AuthzId, User, Password] -> [AuthzId, User, Password];
67+
[AuthzId, User, Password] ->
68+
case parse_domain(AuthzId) of
69+
%% login@domain<NUL>login<NUL>pwd
70+
[AuthzUser, _Domain] -> [AuthzUser, User, Password];
71+
%% login<NUL>login<NUL>pwd
72+
[AuthzUser] -> [AuthzUser, User, Password]
73+
end;
6974
_ -> error
7075
end.
7176

src/ejabberd_auth.erl

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@
3030
-author('alexey@process-one.net').
3131

3232
%% External exports
33-
-export([start/0, set_password/3, check_password/3,
34-
check_password/5, check_password_with_authmodule/3,
35-
check_password_with_authmodule/5, try_register/3,
33+
-export([start/0, set_password/3, check_password/4,
34+
check_password/6, check_password_with_authmodule/4,
35+
check_password_with_authmodule/6, try_register/3,
3636
dirty_get_registered_users/0, get_vh_registered_users/1,
3737
get_vh_registered_users/2, export/1, import/1,
3838
get_vh_registered_users_number/1, import/3,
@@ -61,8 +61,8 @@
6161
-callback remove_user(binary(), binary()) -> any().
6262
-callback remove_user(binary(), binary(), binary()) -> any().
6363
-callback is_user_exists(binary(), binary()) -> boolean() | {error, atom()}.
64-
-callback check_password(binary(), binary(), binary()) -> boolean().
65-
-callback check_password(binary(), binary(), binary(), binary(),
64+
-callback check_password(binary(), binary(), binary(), binary()) -> boolean().
65+
-callback check_password(binary(), binary(), binary(), binary(), binary(),
6666
fun((binary()) -> binary())) -> boolean().
6767
-callback try_register(binary(), binary(), binary()) -> {atomic, atom()} |
6868
{error, atom()}.
@@ -100,26 +100,26 @@ store_type(Server) ->
100100
end,
101101
plain, auth_modules(Server)).
102102

103-
-spec check_password(binary(), binary(), binary()) -> boolean().
103+
-spec check_password(binary(), binary(), binary(), binary()) -> boolean().
104104

105-
check_password(User, Server, Password) ->
106-
case check_password_with_authmodule(User, Server,
105+
check_password(User, AuthzId, Server, Password) ->
106+
case check_password_with_authmodule(User, AuthzId, Server,
107107
Password)
108108
of
109109
{true, _AuthModule} -> true;
110110
false -> false
111111
end.
112112

113113
%% @doc Check if the user and password can login in server.
114-
%% @spec (User::string(), Server::string(), Password::string(),
114+
%% @spec (User::string(), AuthzId::string(), Server::string(), Password::string(),
115115
%% Digest::string(), DigestGen::function()) ->
116116
%% true | false
117-
-spec check_password(binary(), binary(), binary(), binary(),
117+
-spec check_password(binary(), binary(), binary(), binary(), binary(),
118118
fun((binary()) -> binary())) -> boolean().
119-
120-
check_password(User, Server, Password, Digest,
119+
120+
check_password(User, AuthzId, Server, Password, Digest,
121121
DigestGen) ->
122-
case check_password_with_authmodule(User, Server,
122+
case check_password_with_authmodule(User, AuthzId, Server,
123123
Password, Digest, DigestGen)
124124
of
125125
{true, _AuthModule} -> true;
@@ -130,28 +130,28 @@ check_password(User, Server, Password, Digest,
130130
%% The user can login if at least an authentication method accepts the user
131131
%% and the password.
132132
%% The first authentication method that accepts the credentials is returned.
133-
%% @spec (User::string(), Server::string(), Password::string()) ->
133+
%% @spec (User::string(), AuthzId::string(), Server::string(), Password::string()) ->
134134
%% {true, AuthModule} | false
135135
%% where
136136
%% AuthModule = ejabberd_auth_anonymous | ejabberd_auth_external
137137
%% | ejabberd_auth_internal | ejabberd_auth_ldap
138-
%% | ejabberd_auth_odbc | ejabberd_auth_pam
139-
-spec check_password_with_authmodule(binary(), binary(), binary()) -> false |
138+
%% | ejabberd_auth_odbc | ejabberd_auth_pam | ejabberd_auth_riak
139+
-spec check_password_with_authmodule(binary(), binary(), binary(), binary()) -> false |
140140
{true, atom()}.
141141

142-
check_password_with_authmodule(User, Server,
142+
check_password_with_authmodule(User, AuthzId, Server,
143143
Password) ->
144144
check_password_loop(auth_modules(Server),
145-
[User, Server, Password]).
145+
[User, AuthzId, Server, Password]).
146146

147-
-spec check_password_with_authmodule(binary(), binary(), binary(), binary(),
147+
-spec check_password_with_authmodule(binary(), binary(), binary(), binary(), binary(),
148148
fun((binary()) -> binary())) -> false |
149149
{true, atom()}.
150150

151-
check_password_with_authmodule(User, Server, Password,
151+
check_password_with_authmodule(User, AuthzId, Server, Password,
152152
Digest, DigestGen) ->
153153
check_password_loop(auth_modules(Server),
154-
[User, Server, Password, Digest, DigestGen]).
154+
[User, AuthzId, Server, Password, Digest, DigestGen]).
155155

156156
check_password_loop([], _Args) -> false;
157157
check_password_loop([AuthModule | AuthModules], Args) ->

src/ejabberd_auth_anonymous.erl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@
3838

3939

4040
%% Function used by ejabberd_auth:
41-
-export([login/2, set_password/3, check_password/3,
42-
check_password/5, try_register/3,
41+
-export([login/2, set_password/3, check_password/4,
42+
check_password/6, try_register/3,
4343
dirty_get_registered_users/0, get_vh_registered_users/1,
4444
get_vh_registered_users/2, get_vh_registered_users_number/1,
4545
get_vh_registered_users_number/2, get_password_s/2,
@@ -174,11 +174,11 @@ purge_hook(true, LUser, LServer) ->
174174

175175
%% When anonymous login is enabled, check the password for permenant users
176176
%% before allowing access
177-
check_password(User, Server, Password) ->
178-
check_password(User, Server, Password, undefined,
177+
check_password(User, AuthzId, Server, Password) ->
178+
check_password(User, AuthzId, Server, Password, undefined,
179179
undefined).
180180

181-
check_password(User, Server, _Password, _Digest,
181+
check_password(User, _AuthzId, Server, _Password, _Digest,
182182
_DigestGen) ->
183183
case
184184
ejabberd_auth:is_user_exists_in_other_modules(?MODULE,

src/ejabberd_auth_external.erl

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030
-behaviour(ejabberd_auth).
3131

3232
%% External exports
33-
-export([start/1, set_password/3, check_password/3,
34-
check_password/5, try_register/3,
33+
-export([start/1, set_password/3, check_password/4,
34+
check_password/6, try_register/3,
3535
dirty_get_registered_users/0, get_vh_registered_users/1,
3636
get_vh_registered_users/2,
3737
get_vh_registered_users_number/1,
@@ -75,16 +75,20 @@ plain_password_required() -> true.
7575

7676
store_type() -> external.
7777

78-
check_password(User, Server, Password) ->
79-
case get_cache_option(Server) of
80-
false -> check_password_extauth(User, Server, Password);
81-
{true, CacheTime} ->
82-
check_password_cache(User, Server, Password, CacheTime)
78+
check_password(User, AuthzId, Server, Password) ->
79+
if AuthzId /= <<>> andalso AuthzId /= User ->
80+
false;
81+
true ->
82+
case get_cache_option(Server) of
83+
false -> check_password_extauth(User, AuthzId, Server, Password);
84+
{true, CacheTime} ->
85+
check_password_cache(User, AuthzId, Server, Password, CacheTime)
86+
end
8387
end.
8488

85-
check_password(User, Server, Password, _Digest,
89+
check_password(User, AuthzId, Server, Password, _Digest,
8690
_DigestGen) ->
87-
check_password(User, Server, Password).
91+
check_password(User, AuthzId, Server, Password).
8892

8993
set_password(User, Server, Password) ->
9094
case extauth:set_password(User, Server, Password) of
@@ -177,44 +181,44 @@ get_cache_option(Host) ->
177181
CacheTime -> {true, CacheTime}
178182
end.
179183

180-
%% @spec (User, Server, Password) -> true | false
181-
check_password_extauth(User, Server, Password) ->
184+
%% @spec (User, AuthzId, Server, Password) -> true | false
185+
check_password_extauth(User, _AuthzId, Server, Password) ->
182186
extauth:check_password(User, Server, Password) andalso
183187
Password /= <<"">>.
184188

185189
%% @spec (User, Server, Password) -> true | false
186190
try_register_extauth(User, Server, Password) ->
187191
extauth:try_register(User, Server, Password).
188192

189-
check_password_cache(User, Server, Password, 0) ->
190-
check_password_external_cache(User, Server, Password);
191-
check_password_cache(User, Server, Password,
193+
check_password_cache(User, AuthzId, Server, Password, 0) ->
194+
check_password_external_cache(User, AuthzId, Server, Password);
195+
check_password_cache(User, AuthzId, Server, Password,
192196
CacheTime) ->
193197
case get_last_access(User, Server) of
194198
online ->
195-
check_password_internal(User, Server, Password);
199+
check_password_internal(User, AuthzId, Server, Password);
196200
never ->
197-
check_password_external_cache(User, Server, Password);
201+
check_password_external_cache(User, AuthzId, Server, Password);
198202
mod_last_required ->
199203
?ERROR_MSG("extauth is used, extauth_cache is enabled "
200204
"but mod_last is not enabled in that "
201205
"host",
202206
[]),
203-
check_password_external_cache(User, Server, Password);
207+
check_password_external_cache(User, AuthzId, Server, Password);
204208
TimeStamp ->
205209
case is_fresh_enough(TimeStamp, CacheTime) of
206210
%% If no need to refresh, check password against Mnesia
207211
true ->
208-
case check_password_internal(User, Server, Password) of
212+
case check_password_internal(User, AuthzId, Server, Password) of
209213
%% If password valid in Mnesia, accept it
210214
true -> true;
211215
%% Else (password nonvalid in Mnesia), check in extauth and cache result
212216
false ->
213-
check_password_external_cache(User, Server, Password)
217+
check_password_external_cache(User, AuthzId, Server, Password)
214218
end;
215219
%% Else (need to refresh), check in extauth and cache result
216220
false ->
217-
check_password_external_cache(User, Server, Password)
221+
check_password_external_cache(User, AuthzId, Server, Password)
218222
end
219223
end.
220224

@@ -240,8 +244,8 @@ get_password_cache(User, Server, CacheTime) ->
240244
end.
241245

242246
%% Check the password using extauth; if success then cache it
243-
check_password_external_cache(User, Server, Password) ->
244-
case check_password_extauth(User, Server, Password) of
247+
check_password_external_cache(User, AuthzId, Server, Password) ->
248+
case check_password_extauth(User, AuthzId, Server, Password) of
245249
true ->
246250
set_password_internal(User, Server, Password), true;
247251
false -> false
@@ -255,9 +259,9 @@ try_register_external_cache(User, Server, Password) ->
255259
_ -> {error, not_allowed}
256260
end.
257261

258-
%% @spec (User, Server, Password) -> true | false
259-
check_password_internal(User, Server, Password) ->
260-
ejabberd_auth_internal:check_password(User, Server,
262+
%% @spec (User, AuthzId, Server, Password) -> true | false
263+
check_password_internal(User, AuthzId, Server, Password) ->
264+
ejabberd_auth_internal:check_password(User, AuthzId, Server,
261265
Password).
262266

263267
%% @spec (User, Server, Password) -> ok | {error, invalid_jid}

0 commit comments

Comments
 (0)