1 |
|
%%%---------------------------------------------------------------------- |
2 |
|
%%% File : cyrsasl.erl |
3 |
|
%%% Author : Alexey Shchepin <alexey@process-one.net> |
4 |
|
%%% Purpose : Cyrus SASL-like library |
5 |
|
%%% Created : 8 Mar 2003 by Alexey Shchepin <alexey@process-one.net> |
6 |
|
%%% |
7 |
|
%%% |
8 |
|
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne |
9 |
|
%%% |
10 |
|
%%% This program is free software; you can redistribute it and/or |
11 |
|
%%% modify it under the terms of the GNU General Public License as |
12 |
|
%%% published by the Free Software Foundation; either version 2 of the |
13 |
|
%%% License, or (at your option) any later version. |
14 |
|
%%% |
15 |
|
%%% This program is distributed in the hope that it will be useful, |
16 |
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 |
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
18 |
|
%%% General Public License for more details. |
19 |
|
%%% |
20 |
|
%%% You should have received a copy of the GNU General Public License |
21 |
|
%%% along with this program; if not, write to the Free Software |
22 |
|
%%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
23 |
|
%%% |
24 |
|
%%%---------------------------------------------------------------------- |
25 |
|
|
26 |
|
-module(cyrsasl). |
27 |
|
-author('alexey@process-one.net'). |
28 |
|
|
29 |
|
-export([listmech/1, |
30 |
|
server_new/6, |
31 |
|
server_start/4, |
32 |
|
server_step/2, |
33 |
|
default_modules/0]). |
34 |
|
|
35 |
|
-ignore_xref([behaviour_info/1]). |
36 |
|
|
37 |
|
-type sasl_module() :: module(). |
38 |
|
-type mechanism() :: binary(). |
39 |
|
|
40 |
|
-record(sasl_state, {service :: binary(), |
41 |
|
myname :: jid:server(), |
42 |
|
host_type :: binary(), |
43 |
|
realm :: binary(), |
44 |
|
mech_mod :: sasl_module(), |
45 |
|
mech_state :: any(), |
46 |
|
creds :: mongoose_credentials:t() |
47 |
|
}). |
48 |
|
-type sasl_state() :: #sasl_state{}. |
49 |
|
|
50 |
|
% Either a simple error tag or an error tag + <text> field |
51 |
|
-type error() :: {error, binary() | {binary(), binary()}} |
52 |
|
| {error, binary() | {binary(), binary()}, jid:user()}. |
53 |
|
|
54 |
|
-type sasl_result() :: {ok, mongoose_credentials:t()} |
55 |
|
| {continue, binary(), sasl_state()} |
56 |
|
| error(). |
57 |
|
|
58 |
|
-export_type([sasl_module/0, mechanism/0, error/0, sasl_result/0, sasl_state/0]). |
59 |
|
|
60 |
|
-callback mechanism() -> mechanism(). |
61 |
|
|
62 |
|
-callback mech_new(Host :: jid:server(), |
63 |
|
Creds :: mongoose_credentials:t()) -> {ok, tuple()}. |
64 |
|
|
65 |
|
-callback mech_step(State :: tuple(), |
66 |
|
ClientIn :: binary()) -> {ok, mongoose_credentials:t()} |
67 |
|
| cyrsasl:error(). |
68 |
|
|
69 |
|
-optional_callbacks([mechanism/0, mech_new/2]). |
70 |
|
|
71 |
|
-spec check_credentials(sasl_state(), mongoose_credentials:t()) -> R when |
72 |
|
R :: {'ok', mongoose_credentials:t()} |
73 |
|
| {'error', binary()}. |
74 |
|
check_credentials(_State, Creds) -> |
75 |
5860 |
case jid:nodeprep(mongoose_credentials:get(Creds, username, <<>>)) of |
76 |
|
error -> |
77 |
:-( |
{error, <<"not-authorized">>}; |
78 |
|
<<>> -> |
79 |
:-( |
{error, <<"not-authorized">>}; |
80 |
|
_LUser -> |
81 |
5860 |
{ok, Creds} |
82 |
|
end. |
83 |
|
|
84 |
|
-spec listmech(binary()) -> [mechanism()]. |
85 |
|
listmech(HostType) -> |
86 |
11998 |
[M:mechanism() || M <- get_modules(HostType), is_module_supported(HostType, M)]. |
87 |
|
|
88 |
|
-spec server_new(Service :: binary(), |
89 |
|
ServerFQDN :: jid:server(), |
90 |
|
HostType :: binary(), |
91 |
|
UserRealm :: binary(), |
92 |
|
_SecFlags :: [any()], |
93 |
|
Creds :: mongoose_credentials:t()) -> sasl_state(). |
94 |
|
server_new(Service, ServerFQDN, HostType, UserRealm, _SecFlags, Creds) -> |
95 |
6089 |
#sasl_state{service = Service, |
96 |
|
myname = ServerFQDN, |
97 |
|
host_type = HostType, |
98 |
|
realm = UserRealm, |
99 |
|
creds = Creds}. |
100 |
|
|
101 |
|
-spec server_start(State, Mech, ClientIn, SocketData) -> Result when |
102 |
|
State :: sasl_state(), |
103 |
|
Mech :: mechanism(), |
104 |
|
ClientIn :: binary(), |
105 |
|
SocketData :: map(), |
106 |
|
Result :: sasl_result(). |
107 |
|
server_start(#sasl_state{myname = Host, host_type = HostType} = State, |
108 |
|
Mech, ClientIn, SocketData) -> |
109 |
5929 |
case [M || M <- get_modules(HostType), M:mechanism() =:= Mech, |
110 |
5928 |
is_module_supported(HostType, M)] of |
111 |
|
[Module] -> |
112 |
5923 |
{ok, MechState} = Module:mech_new(Host, State#sasl_state.creds, SocketData), |
113 |
5923 |
server_step(State#sasl_state{mech_mod = Module, |
114 |
|
mech_state = MechState}, |
115 |
|
ClientIn); |
116 |
|
[] -> |
117 |
6 |
{error, <<"no-mechanism">>} |
118 |
|
end. |
119 |
|
|
120 |
|
is_module_supported(HostType, cyrsasl_oauth) -> |
121 |
11807 |
gen_mod:is_loaded(HostType, mod_auth_token); |
122 |
|
is_module_supported(HostType, Module) -> |
123 |
147719 |
mongoose_fips:supports_sasl_module(Module) andalso ejabberd_auth:supports_sasl_module(HostType, Module). |
124 |
|
|
125 |
|
-spec server_step(State :: sasl_state(), ClientIn :: binary()) -> Result when |
126 |
|
Result :: sasl_result(). |
127 |
|
server_step(State, ClientIn) -> |
128 |
5971 |
Module = State#sasl_state.mech_mod, |
129 |
5971 |
MechState = State#sasl_state.mech_state, |
130 |
5971 |
case Module:mech_step(MechState, ClientIn) of |
131 |
|
{ok, Creds} -> |
132 |
5860 |
check_credentials(State, Creds); |
133 |
|
{continue, ServerOut, NewMechState} -> |
134 |
51 |
{continue, ServerOut, |
135 |
|
State#sasl_state{mech_state = NewMechState}}; |
136 |
|
{error, Error, Username} -> |
137 |
17 |
{error, Error, Username}; |
138 |
|
{error, Error} -> |
139 |
43 |
{error, Error} |
140 |
|
end. |
141 |
|
|
142 |
|
-spec get_modules(binary()) -> [sasl_module()]. |
143 |
|
get_modules(HostType) -> |
144 |
17927 |
mongoose_config:get_opt([{auth, HostType}, sasl_mechanisms], default_modules()). |
145 |
|
|
146 |
|
default_modules() -> |
147 |
18113 |
[cyrsasl_scram_sha512_plus, |
148 |
|
cyrsasl_scram_sha512, |
149 |
|
cyrsasl_scram_sha384_plus, |
150 |
|
cyrsasl_scram_sha384, |
151 |
|
cyrsasl_scram_sha256_plus, |
152 |
|
cyrsasl_scram_sha256, |
153 |
|
cyrsasl_scram_sha224_plus, |
154 |
|
cyrsasl_scram_sha224, |
155 |
|
cyrsasl_scram_sha1_plus, |
156 |
|
cyrsasl_scram_sha1, |
157 |
|
cyrsasl_plain, |
158 |
|
cyrsasl_anonymous, |
159 |
|
cyrsasl_oauth]. |