1
%% ``The contents of this file are subject to the Erlang Public License,
2
%% Version 1.1, (the "License"); you may not use this file except in
3
%% compliance with the License. You should have received a copy of the
4
%% Erlang Public License along with this software. If not, it can be
5
%% retrieved via the world wide web at http://www.erlang.org/.
7
%% Software distributed under the License is distributed on an "AS IS"
8
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9
%% the License for the specific language governing rights and limitations
12
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
13
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
14
%% AB. All Rights Reserved.''
18
-module(mod_security).
20
%% Security Audit Functionality
23
-export([list_blocked_users/1, list_blocked_users/2, list_blocked_users/3,
24
block_user/4, block_user/5,
25
unblock_user/2, unblock_user/3, unblock_user/4,
26
list_auth_users/1, list_auth_users/2, list_auth_users/3]).
29
-export([do/1, load/2, store/2, remove/1]).
31
-include("httpd.hrl").
33
-define(VMODULE,"SEC").
34
-include("httpd_verbosity.hrl").
39
?vdebug("~n do with ~n Info: ~p",[Info]),
40
%% Check and see if any user has been authorized.
41
case httpd_util:key1search(Info#mod.data,remote_user,not_defined_user) of
43
%% No user has been authorized.
44
case httpd_util:key1search(Info#mod.data, status) of
45
%% A status code has been generated!
46
{401, PhraseArgs, Reason} ->
47
case httpd_util:key1search(Info#mod.parsed_header,
50
%% Not an authorization attempt (server just replied to
51
%% challenge for authentication)
52
{proceed, Info#mod.data};
53
[$B,$a,$s,$i,$c,$ |EncodedString] ->
54
%% Someone tried to authenticate, and obviously failed!
55
?vlog("~n Authentication failed: ~s",
57
report_failed(Info, EncodedString,"Failed authentication"),
58
take_failed_action(Info, EncodedString),
59
{proceed, Info#mod.data}
62
{proceed, Info#mod.data}
65
%% A user has been authenticated, now is he blocked ?
66
?vtrace("user '~p' authentication",[User]),
67
Path = mod_alias:path(Info#mod.data,
69
Info#mod.request_uri),
70
{Dir, SDirData} = secretp(Path, Info#mod.config_db),
71
Addr = httpd_util:lookup(Info#mod.config_db, bind_address),
72
Port = httpd_util:lookup(Info#mod.config_db, port),
73
DF = httpd_util:key1search(SDirData, data_file),
74
case mod_security_server:check_blocked_user(Info, User,
78
?vtrace("user blocked",[]),
79
report_failed(Info,httpd_util:decode_base64(User) ,"User Blocked"),
80
{proceed, [{status, {403, Info#mod.request_uri, ""}}|Info#mod.data]};
82
?vtrace("user not blocked",[]),
83
EncodedUser=httpd_util:decode_base64(User),
84
report_failed(Info, EncodedUser,"Authentication Succedded"),
85
mod_security_server:store_successful_auth(Addr, Port,
87
{proceed, Info#mod.data}
93
report_failed(Info, EncodedString,Event) ->
94
Request = Info#mod.request_line,
95
Decoded = httpd_util:decode_base64(EncodedString),
96
{PortNumber,RemoteHost}=(Info#mod.init_data)#init_data.peername,
97
String = RemoteHost++" : " ++ Event ++ " : "++Request++" : "++Decoded,
98
mod_disk_log:security_log(Info,String),
99
mod_log:security_log(Info, String).
101
take_failed_action(Info, EncodedString) ->
102
Path = mod_alias:path(Info#mod.data,Info#mod.config_db, Info#mod.request_uri),
103
{Dir, SDirData} = secretp(Path, Info#mod.config_db),
104
Addr = httpd_util:lookup(Info#mod.config_db, bind_address),
105
Port = httpd_util:lookup(Info#mod.config_db, port),
106
DecodedString = httpd_util:decode_base64(EncodedString),
107
mod_security_server:store_failed_auth(Info, Addr, Port,
108
DecodedString, SDirData).
110
secretp(Path, ConfigDB) ->
111
Directories = ets:match(ConfigDB,{directory,'$1','_'}),
112
case secret_path(Path, Directories) of
114
SDirs0 = httpd_util:multi_lookup(ConfigDB, security_directory),
115
SDir = lists:filter(fun(X) ->
116
lists:member({path, Directory}, X)
118
{Directory, lists:flatten(SDir)};
120
error_report({internal_error_secretp, ?MODULE}),
124
secret_path(Path,Directories) ->
125
secret_path(Path, httpd_util:uniq(lists:sort(Directories)), to_be_found).
127
secret_path(Path, [], to_be_found) ->
129
secret_path(Path, [], Directory) ->
131
secret_path(Path, [[NewDirectory]|Rest], Directory) ->
132
case regexp:match(Path, NewDirectory) of
133
{match, _, _} when Directory == to_be_found ->
134
secret_path(Path, Rest, NewDirectory);
135
{match, _, Length} when Length > length(Directory)->
136
secret_path(Path, Rest, NewDirectory);
137
{match, _, Length} ->
138
secret_path(Path, Rest, Directory);
140
secret_path(Path, Rest, Directory)
144
load([$<,$D,$i,$r,$e,$c,$t,$o,$r,$y,$ |Directory],[]) ->
145
Dir = httpd_conf:custom_clean(Directory,"",">"),
146
{ok, [{security_directory, Dir, [{path, Dir}]}]};
147
load(eof,[{security_directory,Directory, DirData}|_]) ->
148
{error, ?NICE("Premature end-of-file in "++Directory)};
149
load([$S,$e,$c,$u,$r,$i,$t,$y,$D,$a,$t,$a,$F,$i,$l,$e,$ |FileName],
150
[{security_directory, Dir, DirData}]) ->
151
File = httpd_conf:clean(FileName),
152
{ok, [{security_directory, Dir, [{data_file, File}|DirData]}]};
153
load([$S,$e,$c,$u,$r,$i,$t,$y,$C,$a,$l,$l,$b,$a,$c,$k,$M,$o,$d,$u,$l,$e,$ |ModuleName],
154
[{security_directory, Dir, DirData}]) ->
155
Mod = list_to_atom(httpd_conf:clean(ModuleName)),
156
{ok, [{security_directory, Dir, [{callback_module, Mod}|DirData]}]};
157
load([$S,$e,$c,$u,$r,$i,$t,$y,$M,$a,$x,$R,$e,$t,$r,$i,$e,$s,$ |Retries],
158
[{security_directory, Dir, DirData}]) ->
159
MaxRetries = httpd_conf:clean(Retries),
160
load_return_int_tag("SecurityMaxRetries", max_retries,
161
httpd_conf:clean(Retries), Dir, DirData);
162
load([$S,$e,$c,$u,$r,$i,$t,$y,$B,$l,$o,$c,$k,$T,$i,$m,$e,$ |Time],
163
[{security_directory, Dir, DirData}]) ->
164
load_return_int_tag("SecurityBlockTime", block_time,
165
httpd_conf:clean(Time), Dir, DirData);
166
load([$S,$e,$c,$u,$r,$i,$t,$y,$F,$a,$i,$l,$E,$x,$p,$i,$r,$e,$T,$i,$m,$e,$ |Time],
167
[{security_directory, Dir, DirData}]) ->
168
load_return_int_tag("SecurityFailExpireTime", fail_expire_time,
169
httpd_conf:clean(Time), Dir, DirData);
170
load([$S,$e,$c,$u,$r,$i,$t,$y,$A,$u,$t,$h,$T,$i,$m,$e,$o,$u,$t,$ |Time0],
171
[{security_directory, Dir, DirData}]) ->
172
Time = httpd_conf:clean(Time0),
173
load_return_int_tag("SecurityAuthTimeout", auth_timeout,
174
httpd_conf:clean(Time), Dir, DirData);
175
load([$A,$u,$t,$h,$N,$a,$m,$e,$ |Name0],
176
[{security_directory, Dir, DirData}]) ->
177
Name = httpd_conf:clean(Name0),
178
{ok, [{security_directory, Dir, [{auth_name, Name}|DirData]}]};
179
load("</Directory>",[{security_directory,Directory, DirData}]) ->
180
{ok, [], {security_directory, Directory, DirData}}.
182
load_return_int_tag(Name, Atom, Time, Dir, DirData) ->
185
{ok, [{security_directory, Dir, [{Atom, 99999999999999999999999999999}|DirData]}]};
187
case catch list_to_integer(Time) of
189
{error, Time++" is an invalid "++Name};
191
{ok, [{security_directory, Dir, [{Atom, Val}|DirData]}]}
195
store({security_directory, Dir0, DirData}, ConfigList) ->
196
?CDEBUG("store(security_directory) -> ~n"
200
Addr = httpd_util:key1search(ConfigList, bind_address),
201
Port = httpd_util:key1search(ConfigList, port),
202
mod_security_server:start(Addr, Port),
203
SR = httpd_util:key1search(ConfigList, server_root),
205
case filename:pathtype(Dir0) of
207
filename:join(SR, Dir0);
211
case httpd_util:key1search(DirData, data_file, no_data_file) of
213
{error, no_security_data_file};
216
case filename:pathtype(DataFile0) of
218
filename:join(SR, DataFile0);
222
case mod_security_server:new_table(Addr, Port, DataFile) of
224
NewDirData0 = lists:keyreplace(data_file, 1, DirData,
225
{data_file, TwoTables}),
226
NewDirData1 = case Addr of
228
[{port,Port}|NewDirData0];
230
[{port,Port},{bind_address,Addr}|
233
{ok, {security_directory,NewDirData1}};
235
{error, {{open_data_file, DataFile}, Err}}
241
Addr = case ets:lookup(ConfigDB, bind_address) of
244
[{bind_address, Address}] ->
247
[{port, Port}] = ets:lookup(ConfigDB, port),
248
mod_security_server:delete_tables(Addr, Port),
249
mod_security_server:stop(Addr, Port).
256
%% list_blocked_users
258
list_blocked_users(Port) ->
259
list_blocked_users(undefined, Port).
261
list_blocked_users(Port, Dir) when integer(Port) ->
262
list_blocked_users(undefined,Port,Dir);
263
list_blocked_users(Addr, Port) when integer(Port) ->
264
mod_security_server:list_blocked_users(Addr, Port).
266
list_blocked_users(Addr, Port, Dir) ->
267
mod_security_server:list_blocked_users(Addr, Port, Dir).
272
block_user(User, Port, Dir, Time) ->
273
block_user(User, undefined, Port, Dir, Time).
274
block_user(User, Addr, Port, Dir, Time) ->
275
mod_security_server:block_user(User, Addr, Port, Dir, Time).
280
unblock_user(User, Port) ->
281
unblock_user(User, undefined, Port).
283
unblock_user(User, Port, Dir) when integer(Port) ->
284
unblock_user(User, undefined, Port, Dir);
285
unblock_user(User, Addr, Port) when integer(Port) ->
286
mod_security_server:unblock_user(User, Addr, Port).
288
unblock_user(User, Addr, Port, Dir) ->
289
mod_security_server:unblock_user(User, Addr, Port, Dir).
294
list_auth_users(Port) ->
295
list_auth_users(undefined,Port).
297
list_auth_users(Port, Dir) when integer(Port) ->
298
list_auth_users(undefined, Port, Dir);
299
list_auth_users(Addr, Port) when integer(Port) ->
300
mod_security_server:list_auth_users(Addr, Port).
302
list_auth_users(Addr, Port, Dir) ->
303
mod_security_server:list_auth_users(Addr, Port, Dir).
307
error_logger:error_report(M).