1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2005-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20%%
21
22-module(httpd_mod).
23
24-include_lib("common_test/include/ct.hrl").
25
26%% General testcases bodies called from httpd_SUITE
27-export([alias/4, actions/4, security/5, auth/4, auth_api/6,
28	 auth_mnesia_api/4, htaccess/4,
29	 cgi/4, esi/4, get/4, head/4, all/4]).
30
31%% Help functions
32-export([event/4, ssl_password_cb/0]).
33
34%% Seconds before successful auths timeout.
35-define(AUTH_TIMEOUT,5).
36
37
38%%-------------------------------------------------------------------------
39%% Test cases starts here.
40%%-------------------------------------------------------------------------
41alias(Type, Port, Host, Node) ->
42    %% This is very crude, but...
43    Opts = [],
44    ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node,
45 				       "GET /pics/icon.sheet.gif "
46 				       "HTTP/1.0\r\n\r\n",
47 				       [{statuscode, 200},
48 					{header, "Content-Type","image/gif"},
49 					{header, "Server"},
50 					{header, "Date"},
51 				        {version, "HTTP/1.0"}]),
52
53    ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node,
54 				       "GET / HTTP/1.0\r\n\r\n",
55 				       [{statuscode, 200},
56 					{header, "Content-Type","text/html"},
57 					{header, "Server"},
58 					{header, "Date"},
59 				        {version, "HTTP/1.0"}]),
60
61    ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node,
62 				       "GET /misc/ HTTP/1.0\r\n\r\n",
63 				       [{statuscode, 200},
64 					{header, "Content-Type","text/html"},
65 					{header, "Server"},
66 					{header, "Date"},
67 				        {version, "HTTP/1.0"}]),
68
69    %% Check redirection if trailing slash is missing.
70    ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node,
71 				       "GET /misc HTTP/1.0\r\n\r\n",
72 				       [{statuscode, 301},
73 					{header, "Location"},
74 					{header, "Content-Type","text/html"},
75				        {version, "HTTP/1.0"}]).
76
77%%-------------------------------------------------------------------------
78actions(Type, Port, Host, Node) ->
79    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
80				       "HEAD / HTTP/1.0\r\n\r\n",
81				       [{statuscode, 200},
82				        {version, "HTTP/1.0"}]).
83
84
85%%-------------------------------------------------------------------------
86security(ServerRoot, Type, Port, Host, Node) ->
87
88    global:register_name(mod_security_test, self()),   % Receive events
89
90    ct:sleep(5000),
91
92    OpenDir = filename:join([ServerRoot, "htdocs", "open"]),
93
94    %% Test blocking / unblocking of users.
95
96    %% /open, require user one Aladdin
97
98    remove_users(Node, ServerRoot, Host, Port, "open"),
99
100    auth_request(Type, Host, Port, Node, "/open/", "one", "onePassword",
101		 [{statuscode, 401}]),
102
103    receive_security_event({event, auth_fail, Port, OpenDir,
104			    [{user, "one"}, {password, "onePassword"}]},
105			   Node, Port),
106
107    auth_request(Type,Host,Port,Node,"/open/", "two", "twoPassword",
108		 [{statuscode, 401}]),
109
110    receive_security_event({event, auth_fail, Port, OpenDir,
111			    [{user, "two"}, {password, "twoPassword"}]},
112			   Node, Port),
113    auth_request(Type, Host, Port, Node,"/open/", "Aladdin",
114		 "AladdinPassword", [{statuscode, 401}]),
115
116    receive_security_event({event, auth_fail, Port, OpenDir,
117			    [{user, "Aladdin"},
118			     {password, "AladdinPassword"}]},
119			   Node, Port),
120    add_user(Node, ServerRoot, Port, "open", "one", "onePassword", []),
121
122    add_user(Node, ServerRoot, Port, "open", "two", "twoPassword", []),
123
124    auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword",
125		 [{statuscode, 401}]),
126    receive_security_event({event, auth_fail, Port, OpenDir,
127			    [{user, "one"}, {password, "WrongPassword"}]},
128			   Node, Port),
129    auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword",
130		 [{statuscode, 401}]),
131
132    receive_security_event({event, auth_fail, Port, OpenDir,
133			    [{user, "one"}, {password, "WrongPassword"}]},
134			   Node, Port),
135        receive_security_event({event, user_block, Port, OpenDir,
136			    [{user, "one"}]}, Node, Port),
137
138    global:unregister_name(mod_security_test),   % No more events.
139
140    auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword",
141		 [{statuscode, 401}]),
142    auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword",
143		 [{statuscode, 403}]),
144
145    %% User "one" should be blocked now..
146    case list_blocked_users(Node, Port) of
147	[{"one",_, Port, OpenDir,_}] ->
148	    ok;
149	Blocked ->
150	    exit({unexpected_blocked, Blocked})
151    end,
152
153    [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node, Port, OpenDir),
154
155    true = unblock_user(Node, "one", Port, OpenDir),
156    %% User "one" should not be blocked any more.
157
158    [] = list_blocked_users(Node, Port),
159
160    auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword",
161		 [{statuscode, 200}]),
162
163
164
165    %% Test list_auth_users & auth_timeout
166    ["one"] = list_auth_users(Node, Port),
167
168    auth_request(Type, Host, Port, Node,"/open/", "two", "onePassword",
169		 [{statuscode, 401}]),
170    ["one"] = list_auth_users(Node, Port),
171
172    ["one"] = list_auth_users(Node, Port, OpenDir),
173
174    auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword",
175		 [{statuscode, 401}]),
176
177    ["one"] = list_auth_users(Node, Port),
178
179    ["one"] = list_auth_users(Node, Port, OpenDir),
180
181    %% Wait for successful auth to timeout.
182    ct:sleep(?AUTH_TIMEOUT*1001),
183
184    [] = list_auth_users(Node, Port),
185
186
187    [] = list_auth_users(Node, Port, OpenDir),
188
189    %% "two" is blocked.
190
191    true = unblock_user(Node, "two", Port, OpenDir),
192    %% Test explicit blocking. Block user 'two'.
193
194    [] = list_blocked_users(Node,Port,OpenDir),
195
196    true = block_user(Node, "two", Port, OpenDir, 10),
197    auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword",
198		 [{statuscode, 401}]).
199
200%%-------------------------------------------------------------------------
201auth(Type, Port, Host, Node) ->
202
203    %% Authentication required!
204    ok = httpd_test_lib:verify_request(Type,Host,Port,Node,
205				       "GET /open/ HTTP/1.0\r\n\r\n",
206				       [{statuscode, 401},
207					{version, "HTTP/1.0"},
208					{header, "WWW-Authenticate"}]),
209    ok = httpd_test_lib:verify_request(Type,Host,Port,Node,
210				       "GET /secret/ HTTP/1.0\r\n\r\n",
211				       [{statuscode, 401},
212					{version, "HTTP/1.0"},
213					{header, "WWW-Authenticate"}]),
214    ok = httpd_test_lib:verify_request(Type,Host,Port,Node,
215				       "GET /secret/top_secret/"
216				       " HTTP/1.0\r\n\r\n",
217				       [{statuscode, 401},
218					{version, "HTTP/1.0"},
219					{header, "WWW-Authenticate"}]),
220
221    %% Authentication OK! ["one:OnePassword" user first in user list]
222    auth_request(Type, Host, Port, Node, "/open/dummy.html", "one",
223		 "onePassword", [{statuscode, 200}]),
224    %% Authentication OK and a directory listing is supplied!
225    %% ["Aladdin:open sesame" user second in user list]
226    auth_request(Type, Host, Port, Node, "/open/","Aladdin",
227		 "AladdinPassword", [{statuscode, 200}]),
228
229    %% User correct but wrong password! ["one:one" user first in user list]
230    auth_request(Type, Host, Port, Node, "/open/", "one", "one",
231		 [{statuscode, 401},{header, "WWW-Authenticate"}]),
232    %% Make sure Authenticate header is received even the second time
233    %% we try a incorrect password! Otherwise a browser client will hang!
234    auth_request(Type, Host, Port, Node, "/open/", "one", "one",
235		 [{statuscode, 401},{header, "WWW-Authenticate"}]),
236
237    %% Neither user or password correct! ["dummy:dummy"]
238    auth_request(Type, Host, Port, Node, "/open/", "dummy", "dummy",
239		 [{statuscode, 401}]),
240
241    %% Authentication OK! ["two:TwoPassword" user in first group]
242    auth_request(Type, Host, Port, Node, "/secret/dummy.html", "two",
243		 "twoPassword", [{statuscode, 200}]),
244    %% Authentication OK and a directory listing is supplied!
245    %% ["three:ThreePassword" user in second group]
246    auth_request(Type, Host, Port, Node,"/secret/", "three",
247		 "threePassword", [{statuscode, 200}]),
248
249    %% User correct but wrong password! ["two:two" user in first group]
250    auth_request(Type, Host, Port, Node, "/secret/", "two", "two",
251		 [{statuscode, 401}]),
252    %% Neither user or password correct! ["dummy:dummy"]
253    auth_request(Type, Host, Port, Node,"/secret/", "dummy", "dummy",
254		 [{statuscode, 401}]),
255
256    %% Nested secret/top_secret OK! ["Aladdin:open sesame"]
257    auth_request(Type, Host, Port, Node, "/secret/top_secret/", "Aladdin",
258		 "AladdinPassword", [{statuscode, 200}]),
259    %% Authentication still required!
260    ok =  httpd_test_lib:verify_request(Type, Host, Port, Node, "GET /open/ "
261					"HTTP/1.0\r\n\r\n",
262					[{statuscode, 401},
263					 {version, "HTTP/1.0"},
264					 {header, "WWW-Authenticate"}]),
265    ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "GET /secret/ "
266				       "HTTP/1.0\r\n\r\n",
267				       [{statuscode, 401},
268					{version, "HTTP/1.0"},
269					{header, "WWW-Authenticate"}]),
270    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
271				       "GET /secret/top_secret/ "
272				       "HTTP/1.0\r\n\r\n",
273				       [{statuscode, 401},
274					{version, "HTTP/1.0"},
275					{header, "WWW-Authenticate"}]).
276
277
278%%-------------------------------------------------------------------------
279%% What to test here:
280%%
281%% /open                      - plain,  require user one Aladdin
282%% /secret                    - plain,  require group group1 group2
283%% /secret/top_secret         - plain,  require group group3
284%% /dets_open                 - dets,   require user one Aladdin
285%% /dets_secret               - dets,   require group group1 group2
286%% /dets_secret/top_secret    - dets,   require group group3
287%% /mnesia_open/              - mnesia, require user one Aladdin
288%% /mnesia_secret/            - mnesia, require group group1 group2
289%% /mnesia_secret/top_secret/ - mnesia, require group group3
290auth_api(ServerRoot, AuthStoreType, Type, Port, Host, Node) ->
291    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
292				       "GET / HTTP/1.0\r\n\r\n",
293				       [{statuscode, 200},
294				        {version, "HTTP/1.0"}]),
295    auth_request(Type, Host, Port, Node, "/", "one", "WrongPassword",
296		 [{statuscode, 200}]),
297
298    %% Make sure Authenticate header is received even the second time
299    %% we try a incorrect password! Otherwise a browser client will hang!
300    auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/",
301		 "dummy", "WrongPassword", [{statuscode, 401},
302					    {header, "WWW-Authenticate"}]),
303    auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/",
304		 "dummy", "WrongPassword", [{statuscode, 401},
305					    {header, "WWW-Authenticate"}]),
306
307    %% Change the password to DummyPassword then try to add a user
308    %% Get an error and set it to NoPassword
309    ok = update_password(Node, ServerRoot, Host, Port, AuthStoreType ++
310			 "open", "NoPassword", "DummyPassword"),
311    {error,bad_password} =
312	add_user(Node, ServerRoot, Port, AuthStoreType ++ "open", "one",
313		 "onePassword", []),
314    ok = update_password(Node, ServerRoot, Host, Port, AuthStoreType ++"open",
315			 "DummyPassword", "NoPassword"),
316
317    %% Test /*open, require user one Aladdin
318    remove_users(Node, ServerRoot, Host, Port, AuthStoreType ++ "open"),
319
320    auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/",
321		 "one", "onePassword", [{statuscode, 401}]),
322
323    auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/",
324		 "two", "twoPassword", [{statuscode, 401}]),
325
326    auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/",
327		 "Aladdin", "onePassword", [{statuscode, 401}]),
328
329    add_user(Node, ServerRoot, Port, AuthStoreType ++ "open", "one",
330	     "onePassword", []),
331    add_user(Node, ServerRoot, Port, AuthStoreType ++ "open", "two",
332	     "twoPassword", []),
333    add_user(Node, ServerRoot, Port, AuthStoreType ++ "open", "Aladdin",
334	     "AladdinPassword", []),
335
336    {ok, [_|_]} = list_users(Node, ServerRoot, Host, Port,
337			  AuthStoreType++"open"),
338    auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++ "open/",
339		 "one", "WrongPassword", [{statuscode, 401}]),
340    auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++ "open/",
341		 "one", "onePassword", [{statuscode, 200}]),
342    auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/",
343		 "two", "twoPassword", [{statuscode, 401}]),
344    auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++ "open/",
345		 "Aladdin", "WrongPassword", [{statuscode, 401}]),
346    auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/",
347		 "Aladdin", "AladdinPassword", [{statuscode, 200}]),
348
349    remove_users(Node, ServerRoot, Host, Port, AuthStoreType++"open"),
350    {ok, []} = list_users(Node, ServerRoot, Host, Port,
351			  AuthStoreType++"open"),
352
353    %% Phase 2
354    remove_users(Node, ServerRoot, Host, Port, AuthStoreType++"secret"),
355    {ok, []} = list_users(Node, ServerRoot, Host, Port, AuthStoreType ++
356			  "secret"),
357    auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/",
358		 "one", "onePassword", [{statuscode, 401}]),
359    auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/",
360		 "two", "twoPassword", [{statuscode, 401}]),
361    auth_request(Type, Host, Port,  Node, "/" ++ AuthStoreType ++ "secret/",
362		 "three", "threePassword", [{statuscode, 401}]),
363    add_user(Node, ServerRoot, Port, AuthStoreType ++ "secret", "one",
364	     "onePassword",
365	     []),
366    add_user(Node, ServerRoot, Port, AuthStoreType ++ "secret",
367	     "two", "twoPassword", []),
368    add_user(Node, ServerRoot, Port, AuthStoreType++"secret", "Aladdin",
369	     "AladdinPassword",[]),
370    add_group_member(Node, ServerRoot, Port, AuthStoreType ++ "secret",
371		     "one", "group1"),
372    add_group_member(Node, ServerRoot, Port, AuthStoreType ++ "secret",
373		     "two", "group1"),
374    add_group_member(Node, ServerRoot, Port, AuthStoreType ++
375		     "secret", "Aladdin", "group2"),
376    auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/",
377		 "one", "onePassword", [{statuscode, 200}]),
378    auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/",
379		 "two", "twoPassword", [{statuscode, 200}]),
380    auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/",
381		 "Aladdin", "AladdinPassword", [{statuscode, 200}]),
382    auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/",
383		 "three", "threePassword", [{statuscode, 401}]),
384    remove_users(Node, ServerRoot, Host, Port, AuthStoreType ++ "secret"),
385    {ok, []} = list_users(Node, ServerRoot, Host, Port,
386			  AuthStoreType ++ "secret"),
387    remove_groups(Node, ServerRoot, Host, Port, AuthStoreType ++ "secret"),
388    Directory = filename:join([ServerRoot, "htdocs", AuthStoreType ++
389			       "secret"]),
390    {ok, []} = list_groups(Node, ServerRoot, Host, Port, Directory),
391
392    %% Phase 3
393    remove_users(Node, ServerRoot, Host, Port, AuthStoreType ++
394		 "secret/top_secret"),
395    remove_groups(Node, ServerRoot, Host, Port, AuthStoreType ++
396		  "secret/top_secret"),
397    auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++
398		 "secret/top_secret/",
399		 "three", "threePassword", [{statuscode, 401}]),
400    auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++
401		 "secret/top_secret/", "two", "twoPassword",
402		 [{statuscode, 401}]),
403    add_user(Node, ServerRoot, Port, AuthStoreType ++
404	     "secret/top_secret","three",
405	     "threePassword",[]),
406    add_user(Node, ServerRoot, Port, AuthStoreType ++ "secret/top_secret",
407	     "two","twoPassword", []),
408    add_group_member(Node, ServerRoot, Port, AuthStoreType ++
409		     "secret/top_secret",
410		     "three", "group3"),
411    auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++
412		 "secret/top_secret/", "three", "threePassword",
413		 [{statuscode, 200}]),
414    auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++
415		 "secret/top_secret/", "two", "twoPassword",
416		 [{statuscode, 401}]),
417    add_group_member(Node, ServerRoot, Port, AuthStoreType ++
418		     "secret/top_secret",
419		     "two", "group3"),
420    auth_request(Type,Host,Port,Node,"/" ++ AuthStoreType ++
421		 "secret/top_secret/",
422		 "two", "twoPassword", [{statuscode, 200}]),
423    remove_users(Node, ServerRoot, Host, Port, AuthStoreType ++
424		 "secret/top_secret"),
425    {ok, []} = list_users(Node, ServerRoot, Host, Port,
426			  AuthStoreType ++ "secret/top_secret"),
427    remove_groups(Node, ServerRoot, Host, Port, AuthStoreType ++
428		  "secret/top_secret"),
429    Directory2 = filename:join([ServerRoot, "htdocs",
430				AuthStoreType ++ "secret/top_secret"]),
431    {ok, []} = list_groups(Node, ServerRoot, Host, Port, Directory2),
432    auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++
433		 "secret/top_secret/", "two", "twoPassword",
434		 [{statuscode, 401}]),
435    auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++
436		 "secret/top_secret/","three", "threePassword",
437		 [{statuscode, 401}]).
438
439%%--------------------------------------------------------------------------
440auth_mnesia_api(_Type, Port, _Host, _Node) ->
441    %% Create three groups:
442    %% group1 : one Aladdin
443    %% group2 : two
444    %% group3 : three
445    mod_auth_mnesia:store_user("one", "onePassword", Port,
446			       "/mnesia_open", ""),
447    mod_auth_mnesia:store_user("Aladdin", "AladdinPassword", Port,
448			       "/mnesia_open", ""),
449    mod_auth_mnesia:store_user("two", "twoPassword", Port,
450			       "/mnesia_open", ""),
451    mod_auth_mnesia:store_user("three", "threePassword", Port,
452				     "/mnesia_open", ""),
453    Users = mod_auth_mnesia:list_users(Port, "/mnesia_open"),
454
455    ok = check_lists_members(Users,["Aladdin","one","two","three"]),
456
457    true = mod_auth_mnesia:store_group_member("group1", "one", Port,
458					      "/mnesia_open", ""),
459    true = mod_auth_mnesia:store_group_member("group1","Aladdin", Port,
460					      "/mnesia_open", ""),
461    true = mod_auth_mnesia:store_group_member("group2","two", Port,
462					      "/mnesia_open", ""),
463    true = mod_auth_mnesia:store_group_member("group3","three", Port,
464					      "/mnesia_open", ""),
465    %% Check that all three created groups exist.
466    Groups = mod_auth_mnesia:list_groups(Port, "/mnesia_open"),
467    ok = check_lists_members(Groups, ["group1","group2","group3"]),
468
469    %% Check that the members of all groups are correct.
470    Group1 = mod_auth_mnesia:list_group_members("group1", Port,
471						"/mnesia_open"),
472    ok = check_lists_members(Group1,["one","Aladdin"]),
473    {ok,["two"]}  = mod_auth_mnesia:list_group_members("group2", Port,
474						"/mnesia_open"),
475
476    {ok,["three"]} = mod_auth_mnesia:list_group_members("group3", Port,
477						       "/mnesia_open"),
478
479    %% Delete user 'one' from group one and check that he was removed
480    %% correctly.
481    true = mod_auth_mnesia:remove_group_member("group1", "one", Port,
482					       "/mnesia_open", ""),
483    {ok,["Aladdin"]}  = mod_auth_mnesia:list_group_members("group1", Port,
484							   "/mnesia_open"),
485
486    %% Remove group1 and check that the group was removed correctly.
487    true = mod_auth_mnesia:remove_group("group1", Port, "/mnesia_open", ""),
488    Groups_1 = mod_auth_mnesia:list_groups(Port, "/mnesia_open"),
489    ok = check_lists_members(Groups_1,["group2","group3"]),
490
491    %% Check that the other users still exist in their groups.
492    Users_1 = mod_auth_mnesia:list_users(Port, "/mnesia_open"),
493    ok = check_lists_members(Users_1,["Aladdin","one","two","three"]),
494    {ok,["two"]} = mod_auth_mnesia:list_group_members("group2", Port,
495						  "/mnesia_open"),
496    {ok,["three"]} = mod_auth_mnesia:list_group_members("group3", Port,
497						  "/mnesia_open"),
498
499    %% Remove the remaining groups/users and check that all
500    %% users/groups are removed.
501    true = mod_auth_mnesia:remove_group("group2", Port, "/mnesia_open", ""),
502    true = mod_auth_mnesia:remove_group("group3", Port, "/mnesia_open", ""),
503    {ok, []} = mod_auth_mnesia:list_groups(Port, "/mnesia_open"),
504    true = mod_auth_mnesia:remove_user("one", Port, "/mnesia_open", ""),
505    true = mod_auth_mnesia:remove_user("Aladdin", Port, "/mnesia_open", ""),
506    true = mod_auth_mnesia:remove_user("two", Port, "/mnesia_open", ""),
507    true = mod_auth_mnesia:remove_user("three", Port, "/mnesia_open", ""),
508    {ok, []} = mod_auth_mnesia:list_users(Port, "/mnesia_open"),
509    ok.
510%%--------------------------------------------------------------------------
511htaccess(Type, Port, Host, Node) ->
512    %% Control that authentication required!
513    %% Control that the pages that shall be
514    %% authenticated really need authenticatin
515    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
516				       "GET /ht/open/ HTTP/1.0\r\n\r\n",
517				       [{statuscode, 401},
518					{version, "HTTP/1.0"},
519					{header, "WWW-Authenticate"}]),
520    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
521				       "GET /ht/secret/ HTTP/1.0\r\n\r\n",
522				       [{statuscode, 401},
523					{version, "HTTP/1.0"},
524					{header, "WWW-Authenticate"}]),
525    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
526				       "GET /ht/secret/top_secret/ "
527				       "HTTP/1.0\r\n\r\n",
528				       [{statuscode, 401},
529					{version, "HTTP/1.0"},
530					{header, "WWW-Authenticate"}]),
531
532    %% Make sure Authenticate header is received even the second time
533    %% we try a incorrect password! Otherwise a browser client will hang!
534    auth_request(Type, Host, Port, Node,"/ht/open/",
535		 "dummy", "WrongPassword", [{statuscode, 401},
536					    {header, "WWW-Authenticate"}]),
537    auth_request(Type, Host, Port, Node,"/ht/open/",
538		 "dummy", "WrongPassword", [{statuscode, 401},
539					    {header, "WWW-Authenticate"}]),
540
541    %% Control that not just the first user in the list is valid
542    %% Control the first user
543    %% Authennticating ["one:OnePassword" user first in user list]
544    auth_request(Type, Host, Port, Node, "/ht/open/dummy.html", "one",
545		 "OnePassword", [{statuscode, 200}]),
546
547    %% Control the second user
548    %% Authentication OK and a directory listing is supplied!
549    %% ["Aladdin:open sesame" user second in user list]
550    auth_request(Type, Host, Port, Node, "/ht/open/","Aladdin",
551		 "AladdinPassword", [{statuscode, 200}]),
552
553    %% Contro that bad passwords and userids get a good denial
554    %% User correct but wrong password! ["one:one" user first in user list]
555    auth_request(Type, Host, Port, Node, "/ht/open/", "one", "one",
556		 [{statuscode, 401}]),
557    %% Neither user or password correct! ["dummy:dummy"]
558    auth_request(Type, Host, Port, Node, "/ht/open/", "dummy", "dummy",
559		 [{statuscode, 401}]),
560
561    %% Control that authetication still works, even if its a member in a group
562    %% Authentication OK! ["two:TwoPassword" user in first group]
563    auth_request(Type, Host, Port, Node, "/ht/secret/dummy.html", "two",
564		 "TwoPassword", [{statuscode, 200}]),
565
566    %% Authentication OK and a directory listing is supplied!
567    %% ["three:ThreePassword" user in second group]
568    auth_request(Type, Host, Port, Node,"/ht/secret/", "three",
569		 "ThreePassword", [{statuscode, 200}]),
570
571    %% Deny users with bad passwords even if the user is a group member
572    %% User correct but wrong password! ["two:two" user in first group]
573    auth_request(Type, Host, Port, Node, "/ht/secret/", "two", "two",
574		 [{statuscode, 401}]),
575    %% Neither user or password correct! ["dummy:dummy"]
576    auth_request(Type, Host, Port, Node,"/ht/secret/", "dummy", "dummy",
577		 [{statuscode, 401}]),
578
579    %% control that we deny the users that are in subnet above the allowed
580    auth_request(Type, Host, Port, Node,"/ht/blocknet/dummy.html", "four",
581		 "FourPassword", [{statuscode, 403}]),
582    %% Control that we only applies the rules to the right methods
583    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
584				       "HEAD /ht/blocknet/dummy.html"
585				       " HTTP/1.0\r\n\r\n",
586				       [{statuscode, 200},
587					{version, "HTTP/1.0"}]),
588
589    %% Control that the rerquire directive can be overrideen
590    auth_request(Type, Host, Port, Node,
591		 "/ht/secret/top_secret/", "Aladdin", "AladdinPassword",
592		 [{statuscode, 401}]),
593
594    %% Authentication still required!
595    ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "GET /ht/open/ "
596				       "HTTP/1.0\r\n\r\n",
597				       [{statuscode, 401},
598					{version, "HTTP/1.0"},
599					{header, "WWW-Authenticate"}]),
600    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
601				       "GET /ht/secret/ HTTP/1.0\r\n\r\n",
602				       [{statuscode, 401},
603					{version, "HTTP/1.0"},
604					{header, "WWW-Authenticate"}]),
605    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
606				       "GET /ht/secret/top_secret/ "
607				       "HTTP/1.0\r\n\r\n",
608				       [{statuscode, 401},
609					{version, "HTTP/1.0"},
610					{header, "WWW-Authenticate"}]).
611%%--------------------------------------------------------------------
612cgi(Type, Port, Host, Node) ->
613    {Script, Script2, Script3} =
614	case test_server:os_type() of
615	    {win32, _} ->
616		{"printenv.bat", "printenv.sh", "cgi_echo.exe"};
617	    _ ->
618		{"printenv.sh", "printenv.bat", "cgi_echo"}
619	end,
620
621    %% The length (> 100) is intentional
622    ok = httpd_test_lib:
623	verify_request(Type, Host, Port, Node,
624		       "POST /cgi-bin/" ++ Script3 ++
625		       " HTTP/1.0\r\n"
626		       "Content-Length:100 \r\n\r\n "
627		       "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
628		       "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
629		       "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
630		       "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
631		       "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
632		       "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
633		       "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
634		       "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
635		       "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
636		       "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
637		       "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
638		       "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
639		       "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
640		       "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
641		       "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
642		       "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
643		       "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
644		       " \r\n\r\n",
645		       [{statuscode, 200},
646			{version, "HTTP/1.0"},
647			{header, "content-type", "text/plain"}]),
648
649    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
650				       "GET /cgi-bin/"++ Script ++
651				       " HTTP/1.0\r\n\r\n",
652				       [{statuscode, 200},
653				       {version, "HTTP/1.0"}]),
654
655    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
656				       "GET /cgi-bin/not_there "
657				       "HTTP/1.0\r\n\r\n",
658				       [{statuscode, 404},{statuscode, 500},
659				       {version, "HTTP/1.0"}]),
660
661    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
662				       "GET /cgi-bin/"++ Script ++
663				       "?Nisse:kkk?sss/lll HTTP/1.0\r\n\r\n",
664				       [{statuscode, 200},
665					{version, "HTTP/1.0"}]),
666
667    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
668				       "POST /cgi-bin/"++ Script ++
669				       " HTTP/1.0\r\n\r\n",
670				       [{statuscode, 200},
671					{version, "HTTP/1.0"}]),
672
673    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
674				       "GET /htbin/"++ Script ++
675				       " HTTP/1.0\r\n\r\n",
676				       [{statuscode, 200},
677				       {version, "HTTP/1.0"}]),
678    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
679				       "GET /htbin/not_there "
680				       "HTTP/1.0\r\n\r\n",
681				       [{statuscode, 404},{statuscode, 500},
682				        {version, "HTTP/1.0"}]),
683
684    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
685				       "GET /htbin/"++ Script ++
686				       "?Nisse:kkk?sss/lll HTTP/1.0\r\n\r\n",
687				       [{statuscode, 200},
688					{version, "HTTP/1.0"}]),
689    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
690				       "POST /htbin/"++ Script ++
691				       " HTTP/1.0\r\n\r\n",
692				       [{statuscode, 200},
693				       {version, "HTTP/1.0"}]),
694    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
695				       "POST /htbin/"++ Script ++
696				       " HTTP/1.0\r\n\r\n",
697				       [{statuscode, 200},
698				       {version, "HTTP/1.0"}]),
699
700    %% Execute an existing, but bad CGI script..
701    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
702				       "POST /htbin/"++ Script2 ++
703				       " HTTP/1.0\r\n\r\n",
704				       [{statuscode, 404},
705					{version, "HTTP/1.0"}]),
706
707    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
708				       "POST /cgi-bin/"++ Script2 ++
709				       " HTTP/1.0\r\n\r\n",
710				       [{statuscode, 404},
711				       {version, "HTTP/1.0"}]),
712
713    %% Check "ScriptNoCache" directive (default: false)
714    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
715				       "GET /cgi-bin/" ++ Script ++
716				       " HTTP/1.0\r\n\r\n",
717				       [{statuscode, 200},
718					{no_header, "cache-control"},
719					{version, "HTTP/1.0"}]).
720
721%%--------------------------------------------------------------------
722esi(Type, Port, Host, Node) ->
723    %% Check "ErlScriptAlias" and "EvalScriptAlias" directives
724     ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
725 				       "GET /eval?httpd_example:print(\"Hi!\")"
726 				       " HTTP/1.0\r\n\r\n",
727 				       [{statuscode, 200},
728 				       {version, "HTTP/1.0"}]),
729     ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
730 				       "GET /eval?not_allowed:print(\"Hi!\")"
731 				       " HTTP/1.0\r\n\r\n",
732 				       [{statuscode, 403},
733 				       {version, "HTTP/1.0"}]),
734     ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
735 				       "GET /eval?httpd_example:undef(\"Hi!\")"
736 				       " HTTP/1.0\r\n\r\n",
737 				       [{statuscode, 500},
738 					{version, "HTTP/1.0"}]),
739     ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
740 				       "GET /cgi-bin/erl/httpd_example "
741  				       "HTTP/1.0\r\n\r\n",
742  				       [{statuscode, 400},
743 					{version, "HTTP/1.0"}]),
744     ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
745 				       "GET /cgi-bin/erl/httpd_example:get "
746 				       "HTTP/1.0\r\n\r\n",
747 				       [{statuscode, 200},
748 				       {version, "HTTP/1.0"}]),
749     ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
750 				       "GET /cgi-bin/erl/httpd_example:"
751 				       "get?input=4711"
752 				       " HTTP/1.0\r\n\r\n",
753 				       [{statuscode, 200},
754 				       {version, "HTTP/1.0"}]),
755    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
756				       "GET /cgi-bin/erl/httpd_example:"
757				       "post HTTP/1.0\r\n\r\n",
758				       [{statuscode, 200},
759					{version, "HTTP/1.0"}]),
760    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
761				       "GET /cgi-bin/erl/not_allowed:post "
762				       "HTTP/1.0\r\n\r\n",
763				       [{statuscode, 403},
764				       {version, "HTTP/1.0"}]),
765    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
766				       "GET /cgi-bin/erl/httpd_example:undef "
767				       "HTTP/1.0\r\n\r\n",
768				       [{statuscode, 404},
769				       {version, "HTTP/1.0"}]),
770    ok =  httpd_test_lib:verify_request(Type, Host, Port, Node,
771					"GET /cgi-bin/erl/httpd_example/yahoo"
772					" HTTP/1.0\r\n\r\n",
773					[{statuscode, 302},
774					{version, "HTTP/1.0"}]),
775    %% Check "ErlScriptNoCache" directive (default: false)
776    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
777				       "GET /cgi-bin/erl/httpd_example:get"
778				       " HTTP/1.0\r\n\r\n",
779				       [{statuscode, 200},
780					{no_header, "cache-control"},
781					{version, "HTTP/1.0"}]),
782    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
783				       "GET /cgi-bin/erl/httpd_example:new_status_and_location"
784				       " HTTP/1.1\r\n\r\n",
785				       [{statuscode, 201},
786                                        {header, "Location"},
787					{version, "HTTP/1.1"}]),
788    ok.
789
790%%--------------------------------------------------------------------
791get(Type, Port, Host, Node) ->
792    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
793				       "GET /index.html HTTP/1.0\r\n\r\n",
794				       [{statuscode, 200},
795					{header, "Content-Type", "text/html"},
796					{header, "Date"},
797					{header, "Server"},
798					{version, "HTTP/1.0"}]),
799    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
800				       "GET /fsize.shtml HTTP/1.1\r\nHost:"
801				       ++ Host ++ "\r\n\r\n",
802				       [{statuscode, 200},
803					{header, "Content-Type", "text/html"},
804					{header, "Date"},
805					{header, "Server"}]),
806    ok =  httpd_test_lib:verify_request(Type, Host, Port, Node,
807					"GET /fsize.shtml HTTP/1.0\r\n\r\n",
808					[{statuscode, 200},
809					 {header, "Content-Type"},
810					 {header, "Server"},
811					 {header, "Date"},
812					{version, "HTTP/1.0"}]),
813    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
814				       "GET /secret/dummy.html "
815				       "HTTP/1.0\r\n\r\n",
816				       [{statuscode, 401},
817					{header, "WWW-Authenticate"},
818					{version, "HTTP/1.0"}]),
819    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
820				       "GET /index.html HTTP/1.0\r\n\r\n",
821				       [{statuscode, 200},
822					{header, "Server"},
823					{header, "Date"},
824					{header, "Content-Type",
825					 "text/html"},
826				       {version, "HTTP/1.0"}]),
827    ok.
828
829%%--------------------------------------------------------------------
830head(Type, Port, Host, Node) ->
831    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
832				       "HEAD /index.html HTTP/1.0\r\n\r\n",
833				       [{statuscode, 200},
834					{version, "HTTP/1.0"}]),
835    ok.
836%%--------------------------------------------------------------------
837all(Type, Port, Host, Node) ->
838    actions(Type, Port, Host, Node),
839    alias(Type, Port, Host, Node),
840    auth(Type, Port, Host, Node),
841    cgi(Type, Port, Host, Node),
842    esi(Type, Port, Host, Node),
843    get(Type, Port, Host, Node),
844    head(Type, Port, Host, Node),
845    ok.
846
847%%--------------------------------------------------------------------
848%% Internal functions
849%%--------------------------------------------------------------------
850auth_request(Type, Host, Port, Node, URI, User, Passwd, Expect) ->
851    Req = ["GET ", URI, " HTTP/1.0\r\n",
852	   "Authorization: Basic ",
853	   base64:encode_to_string(User++":"++Passwd),
854	   "\r\n\r\n"],
855    ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
856				       lists:flatten(Req),
857				       [{version, "HTTP/1.0"} | Expect]).
858
859remove_users(Node, ServerRoot, Host, Port, Dir) ->
860    %% List users, delete them, and make sure they are gone.
861    case list_users(Node, ServerRoot, Host, Port, Dir) of
862	{ok, Users} ->
863	    lists:foreach(fun(User) ->
864				  delete_user(Node, ServerRoot, Host,
865					      Port, Dir, User)
866			  end,
867			  Users),
868		  {ok, []} = list_users(Node, ServerRoot, Host, Port, Dir);
869	_ ->
870	    ok
871    end.
872
873add_user(Node, Root, Port, Dir, User, Password, UserData) ->
874    Addr = undefined,
875    Directory = filename:join([Root, "htdocs", Dir]),
876    rpc:call(Node, mod_auth, add_user,
877	     [User, Password, UserData, Addr, Port, Directory]).
878
879delete_user(Node, Root, _Host, Port, Dir, User) ->
880    Addr = undefined,
881    Directory = filename:join([Root, "htdocs", Dir]),
882    rpc:call(Node, mod_auth, delete_user, [User, Addr, Port, Directory]).
883
884list_users(Node, Root, _Host, Port, Dir) ->
885    Addr = undefined,
886    Directory = filename:join([Root, "htdocs", Dir]),
887    rpc:call(Node, mod_auth, list_users, [Addr, Port, Directory]).
888
889
890receive_security_event(Event, Node, Port) ->
891    receive
892	Event ->
893	    ok;
894	{'EXIT', _, _} ->
895	    receive_security_event(Event, Node, Port)
896    after 5000 ->
897	    %% Flush the message queue, to see if we got something...
898	    Msgs = inets_test_lib:flush(),
899	    ct:fail({expected_event_not_received, Msgs})
900
901    end.
902
903%% receive_security_event(Event, Node, Port) ->
904%%     io:format(user, "~w:receive_security_event -> entry with"
905%% 	      "~n   Event: ~p"
906%% 	      "~n   Node:  ~p"
907%% 	      "~n   Port:  ~p"
908%% 	      "~n", [?MODULE, Event, Node, Port]),
909%%     receive
910%% 	Event ->
911%% 	    ok;
912%% 	{'EXIT', _, _} ->
913%% 	    receive_security_event(Event, Node, Port);
914%% 	Other ->
915%% 	    ct:fail({unexpected_event,
916%% 			      {expected, Event}, {received, Other}})
917%%     after 5000 ->
918%% 	    ct:fail(no_event_recived)
919
920%%     end.
921
922list_blocked_users(Node,Port) ->
923    Addr = undefined, % Assumed to be on the same host
924    rpc:call(Node, mod_security, list_blocked_users, [Addr,Port]).
925
926list_blocked_users(Node,Port,Dir) ->
927    Addr = undefined, % Assumed to be on the same host
928    rpc:call(Node, mod_security, list_blocked_users, [Addr,Port,Dir]).
929
930block_user(Node,User,Port,Dir,Sec) ->
931    Addr = undefined, % Assumed to be on the same host
932    rpc:call(Node, mod_security, block_user, [User, Addr, Port, Dir, Sec]).
933
934unblock_user(Node,User,Port,Dir) ->
935    Addr = undefined, % Assumed to be on the same host
936    rpc:call(Node, mod_security, unblock_user, [User, Addr, Port, Dir]).
937
938list_auth_users(Node,Port) ->
939    Addr = undefined, % Assumed to be on the same host
940    rpc:call(Node, mod_security, list_auth_users, [Addr,Port]).
941
942list_auth_users(Node,Port,Dir) ->
943    Addr = undefined, % Assumed to be on the same host
944    rpc:call(Node, mod_security, list_auth_users, [Addr,Port,Dir]).
945
946update_password(Node, ServerRoot, _Address, Port, Dir, Old, New)->
947    Directory = filename:join([ServerRoot, "htdocs", Dir]),
948    rpc:call(Node, mod_auth, update_password,
949	     [undefined, Port, Directory, Old, New, New]).
950
951remove_groups(Node, ServerRoot, Host, Port, Dir) ->
952    Directory = filename:join([ServerRoot, "htdocs", Dir]),
953    {ok, Groups} = list_groups(Node, ServerRoot, Host, Port, Directory),
954    lists:foreach(fun(Group) ->
955				delete_group(Node, Group, Port, Directory)
956			end,
957			Groups),
958    {ok, []} = list_groups(Node, ServerRoot, Host, Port, Directory),
959    ok.
960
961delete_group(Node, Group, Port, Dir) ->
962    Addr = undefined,
963    rpc:call(Node, mod_auth, delete_group, [Group, Addr, Port, Dir]).
964
965list_groups(Node, _, _, Port, Dir) ->
966    Addr = undefined,
967    rpc:call(Node, mod_auth, list_groups, [Addr, Port, Dir]).
968
969add_group_member(Node, ServerRoot, Port, Dir, User, Group) ->
970    Addr = undefined,
971    rpc:call(Node, mod_auth, add_group_member, [Group, User, Addr, Port,
972						filename:join(
973						  [ServerRoot,
974						   "htdocs",Dir])]).
975event(What, Port, Dir, Data) ->
976    Msg = {event, What, Port, Dir, Data},
977    case global:whereis_name(mod_security_test) of
978	undefined ->
979	    ok;
980	_Pid ->
981	    global:send(mod_security_test, Msg)
982    end.
983
984ssl_password_cb() ->
985    "dummy-ssl-password".
986
987check_lists_members({ok,L},L) ->
988    ok;
989check_lists_members({ok,L1},L2) ->
990    check_lists_members1(lists:sort(L1),lists:sort(L2));
991check_lists_members(Error,_L) ->
992    Error.
993
994check_lists_members1(L,L) ->
995    ok;
996check_lists_members1(L1,L2) ->
997    {error,{lists_not_equal,L1,L2}}.
998
999
1000
1001