1 |
|
%%%---------------------------------------------------------------------- |
2 |
|
%%% File : extauth.erl |
3 |
|
%%% Author : Leif Johansson <leifj@it.su.se> |
4 |
|
%%% Purpose : External authentication using a simple port-driver |
5 |
|
%%% Created : 30 Jul 2004 by Leif Johansson <leifj@it.su.se> |
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(extauth). |
27 |
|
-author('leifj@it.su.se'). |
28 |
|
|
29 |
|
-export([start/2, |
30 |
|
stop/1, |
31 |
|
init/2, |
32 |
|
check_password/4, |
33 |
|
set_password/4, |
34 |
|
try_register/4, |
35 |
|
remove_user/3, |
36 |
|
does_user_exist/3]). |
37 |
|
|
38 |
|
-include("mongoose.hrl"). |
39 |
|
|
40 |
|
-define(INIT_TIMEOUT, 60000). % Timeout is in milliseconds: 60 seconds == 60000 |
41 |
|
-define(CALL_TIMEOUT, 10000). % Timeout is in milliseconds: 10 seconds == 10000 |
42 |
|
|
43 |
|
|
44 |
|
-spec start(mongooseim:host_type(), _) -> 'ok'. |
45 |
|
start(HostType, ExtPrg) -> |
46 |
:-( |
lists:foreach( |
47 |
|
fun(This) -> |
48 |
:-( |
start_instance(get_process_name(HostType, This), ExtPrg) |
49 |
|
end, |
50 |
|
lists:seq(0, get_instances(HostType) - 1) |
51 |
|
). |
52 |
|
|
53 |
|
|
54 |
|
-spec start_instance(atom(), _) -> pid(). |
55 |
|
start_instance(ProcessName, ExtPrg) -> |
56 |
:-( |
spawn(?MODULE, init, [ProcessName, ExtPrg]). |
57 |
|
|
58 |
|
|
59 |
|
-spec restart_instance(atom(), _) -> pid(). |
60 |
|
restart_instance(ProcessName, ExtPrg) -> |
61 |
:-( |
unregister(ProcessName), |
62 |
:-( |
start_instance(ProcessName, ExtPrg). |
63 |
|
|
64 |
|
|
65 |
|
-spec init(atom(), string()) -> no_return(). |
66 |
|
init(ProcessName, ExtPrg) -> |
67 |
:-( |
register(ProcessName, self()), |
68 |
:-( |
process_flag(trap_exit, true), |
69 |
:-( |
Port = open_port({spawn, ExtPrg}, [{packet, 2}]), |
70 |
:-( |
loop(Port, ?INIT_TIMEOUT, ProcessName, ExtPrg). |
71 |
|
|
72 |
|
|
73 |
|
-spec stop(atom() | binary()) -> 'ok'. |
74 |
|
stop(HostType) -> |
75 |
:-( |
lists:foreach( |
76 |
|
fun(This) -> |
77 |
:-( |
get_process_name(HostType, This) ! stop |
78 |
|
end, |
79 |
|
lists:seq(0, get_instances(HostType) - 1) |
80 |
|
). |
81 |
|
|
82 |
|
|
83 |
|
-spec get_process_name(binary(), integer()) -> atom(). |
84 |
|
get_process_name(HostType, Integer) -> |
85 |
:-( |
gen_mod:get_module_proc(lists:append([erlang:binary_to_list(HostType), |
86 |
|
integer_to_list(Integer)]), eauth). |
87 |
|
|
88 |
|
|
89 |
|
-spec check_password(mongooseim:host_type(), jid:user(), jid:server(), binary()) -> boolean(). |
90 |
|
check_password(HostType, User, Server, Password) -> |
91 |
:-( |
call_port(HostType, [<<"auth">>, User, Server, Password]). |
92 |
|
|
93 |
|
|
94 |
|
-spec does_user_exist(mongooseim:host_type(), jid:user(), jid:server()) -> boolean(). |
95 |
|
does_user_exist(HostType, User, Server) -> |
96 |
:-( |
call_port(HostType, [<<"isuser">>, User, Server]). |
97 |
|
|
98 |
|
|
99 |
|
-spec set_password(mongooseim:host_type(), jid:user(), jid:server(), binary()) -> any(). |
100 |
|
set_password(HostType, User, Server, Password) -> |
101 |
:-( |
call_port(HostType, [<<"setpass">>, User, Server, Password]). |
102 |
|
|
103 |
|
|
104 |
|
-spec try_register(mongooseim:host_type(), jid:user(), jid:server(), binary()) -> |
105 |
|
ok | {error, not_allowed}. |
106 |
|
try_register(HostType, User, Server, Password) -> |
107 |
:-( |
case call_port(HostType, [<<"tryregister">>, User, Server, Password]) of |
108 |
:-( |
true -> ok; |
109 |
:-( |
false -> {error, not_allowed} |
110 |
|
end. |
111 |
|
|
112 |
|
|
113 |
|
-spec remove_user(mongooseim:host_type(), jid:user(), jid:server()) -> any(). |
114 |
|
remove_user(HostType, User, Server) -> |
115 |
:-( |
call_port(HostType, [<<"removeuser">>, User, Server]). |
116 |
|
|
117 |
|
-spec call_port(mongooseim:host_type(), [any(), ...]) -> any(). |
118 |
|
call_port(HostType, Msg) -> |
119 |
:-( |
ProcessName = get_process_name(HostType, random_instance(get_instances(HostType))), |
120 |
:-( |
ProcessName ! {call, self(), Msg}, |
121 |
:-( |
receive |
122 |
|
{eauth, Result} -> |
123 |
:-( |
Result |
124 |
|
end. |
125 |
|
|
126 |
|
|
127 |
|
-spec random_instance(pos_integer()) -> non_neg_integer(). |
128 |
|
random_instance(MaxNum) -> |
129 |
:-( |
rand:uniform(MaxNum) - 1. |
130 |
|
|
131 |
|
|
132 |
|
-spec get_instances(mongooseim:host_type()) -> integer(). |
133 |
|
get_instances(HostType) -> |
134 |
:-( |
mongoose_config:get_opt([{auth, HostType}, external, instances]). |
135 |
|
|
136 |
|
|
137 |
|
-spec loop(port(), integer(), atom(), any()) -> no_return(). |
138 |
|
loop(Port, Timeout, ProcessName, ExtPrg) -> |
139 |
:-( |
receive |
140 |
|
{call, Caller, Msg} -> |
141 |
:-( |
port_command(Port, encode(Msg)), |
142 |
:-( |
receive |
143 |
|
{Port, {data, Data}} -> |
144 |
:-( |
?LOG_DEBUG(#{what => extauth_result, |
145 |
|
extauth_call => Msg, result => Data, |
146 |
:-( |
text => <<"extauth call received data response">>}), |
147 |
:-( |
Caller ! {eauth, decode(Data)}, |
148 |
:-( |
loop(Port, ?CALL_TIMEOUT, ProcessName, ExtPrg); |
149 |
|
{Port, Other} -> |
150 |
:-( |
?LOG_ERROR(#{what => extauth_unexpected_message, |
151 |
|
extauth_call => Msg, unexpected_message => Other, |
152 |
:-( |
text => <<"extauth call received strange response">>}), |
153 |
:-( |
Caller ! {eauth, false}, |
154 |
:-( |
loop(Port, ?CALL_TIMEOUT, ProcessName, ExtPrg) |
155 |
|
after |
156 |
|
Timeout -> |
157 |
:-( |
?LOG_ERROR(#{what => extauth_timeout, extauth_call => Msg, |
158 |
:-( |
text => <<"extauth call didn't receive response, restarting instance">>}), |
159 |
:-( |
Caller ! {eauth, false}, |
160 |
:-( |
Pid = restart_instance(ProcessName, ExtPrg), |
161 |
:-( |
flush_buffer_and_forward_messages(Pid), |
162 |
:-( |
exit(port_terminated) |
163 |
|
end; |
164 |
|
stop -> |
165 |
:-( |
Port ! {self(), close}, |
166 |
:-( |
receive |
167 |
|
{Port, closed} -> |
168 |
:-( |
exit(normal) |
169 |
|
end; |
170 |
|
{'EXIT', Port, Reason} -> |
171 |
:-( |
?LOG_CRITICAL(#{what => extauth_crash, |
172 |
|
text => <<"extauth script has exitted abruptly, restarting instance">>, |
173 |
:-( |
reason => Reason}), |
174 |
:-( |
Pid = restart_instance(ProcessName, ExtPrg), |
175 |
:-( |
flush_buffer_and_forward_messages(Pid), |
176 |
:-( |
exit(port_terminated) |
177 |
|
end. |
178 |
|
|
179 |
|
|
180 |
|
-spec flush_buffer_and_forward_messages(pid()) -> 'true'. |
181 |
|
flush_buffer_and_forward_messages(Pid) -> |
182 |
:-( |
receive |
183 |
|
Message -> |
184 |
:-( |
Pid ! Message, |
185 |
:-( |
flush_buffer_and_forward_messages(Pid) |
186 |
|
after 0 -> |
187 |
:-( |
true |
188 |
|
end. |
189 |
|
|
190 |
|
|
191 |
|
-spec join([binary()], binary()) -> binary(). |
192 |
|
join(List, Sep) -> |
193 |
:-( |
lists:foldl(fun(A, <<"">>) -> A; |
194 |
:-( |
(A, Acc) -> <<Acc/bitstring, Sep/bitstring, A/bitstring>> |
195 |
|
end, <<"">>, List). |
196 |
|
|
197 |
|
|
198 |
|
-spec encode([binary()]) -> [byte()]. |
199 |
|
encode(L) -> |
200 |
:-( |
erlang:binary_to_list(join(L, <<":">>)). |
201 |
|
|
202 |
|
|
203 |
|
-spec decode([0 | 1, ...]) -> boolean(). |
204 |
:-( |
decode([0, 0]) -> false; |
205 |
:-( |
decode([0, 1]) -> true. |
206 |
|
|