diff options
author | Ingela Anderton Andin <ingela@erlang.org> | 2019-11-21 17:11:36 +0100 |
---|---|---|
committer | Ingela Anderton Andin <ingela@erlang.org> | 2019-12-13 15:19:27 +0100 |
commit | 7d209cffbe26d184a24980944f1dec873a610332 (patch) | |
tree | 7b23564238c311a61908ef8b51632473f367d6d9 /lib/inets | |
parent | 758f472059f6e6e6d8d9112be0341b5263f00811 (diff) | |
download | erlang-7d209cffbe26d184a24980944f1dec873a610332.tar.gz |
inets: httpd - Remove legacy security model
This is not secure and should not be used.
Also reduces maintainance burden.
Diffstat (limited to 'lib/inets')
-rw-r--r-- | lib/inets/doc/src/http_server.xml | 177 | ||||
-rw-r--r-- | lib/inets/doc/src/httpd.xml | 14 | ||||
-rw-r--r-- | lib/inets/src/http_server/Makefile | 1 | ||||
-rw-r--r-- | lib/inets/src/http_server/mod_htaccess.erl | 1071 | ||||
-rw-r--r-- | lib/inets/src/inets_app/inets.app.src | 1 | ||||
-rw-r--r-- | lib/inets/test/httpd_SUITE.erl | 258 | ||||
-rw-r--r-- | lib/inets/test/httpd_mod_SUITE.erl | 2 |
7 files changed, 7 insertions, 1517 deletions
diff --git a/lib/inets/doc/src/http_server.xml b/lib/inets/doc/src/http_server.xml index 65b3dcde95..6a7be73a4e 100644 --- a/lib/inets/doc/src/http_server.xml +++ b/lib/inets/doc/src/http_server.xml @@ -171,162 +171,9 @@ </section> <section> - <title>Htaccess - User Configurable Authentication</title> - <marker id="htaccess"></marker> - <p>Web server users without server administrative privileges - that need to manage authentication of web pages that are local - to their user can use the per-directory runtime configurable - user-authentication scheme <c>htaccess</c>. - It works as follows:</p> - <list type="bulleted"> - <item>Each directory in the path to the requested asset is - searched for an access file (default is <c>.htaccess</c>), which - restricts the web servers rights to respond to a request. - If an access file is found, the rules in that file is applied to the - request.</item> - <item>The rules in an access file apply to files in the same - directory and in subdirectories. If there exists more than one - access file in the path to an asset, the rules in the - access file nearest the requested asset is applied.</item> - <item>To change the rules that restrict the use of - an asset, the user only needs write access - to the directory where the asset is.</item> - <item>All access files in the path to a requested asset are read - once per request. This means that the load on the server - increases when <c>htaccess</c> is used.</item> - <item>If a directory is limited both by authentication directives - in the HTTP server configuration file and by the <c>htaccess</c> - files, the user must be allowed to get access to the file by both - methods for the request to succeed.</item> - </list> - - <section> - <title>Access Files Directives</title> - <p>In every directory under <c>DocumentRoot</c> or under an - <c>Alias</c> a user can place an access file. An access file - is a plain text file that specifies the restrictions to - consider before the web server answers to a - request. If there are more than one access file in the path - to the requested asset, the directives in the access file in - the directory nearest the asset is used.</p> - <taglist> - <tag><em>"allow"</em></tag> - <item> - <p><em>Syntax:</em> <c>Allow</c> from subnet <c>subnet | from all</c></p> - <p><em>Default:</em> <c>from all</c></p> - <p>Same as directive <c>allow</c> for the server configuration file.</p> - </item> - <tag><em>"AllowOverRide"</em></tag> - <item> - <p><em>Syntax:</em> <c>AllowOverRide</c> <c>all | none | Directives</c></p> - <p><em>Default:</em> <c>none</c></p> - <p><c>AllowOverRide</c> specifies the parameters that - access files in subdirectories are not allowed to alter the value - for. If the parameter is set to <c>none</c>, no further - access files is parsed. - </p> - <p>If only one access file exists, setting this parameter to - <c>none</c> can ease the burden on the server as the server - then stops looking for access files.</p> - </item> - <tag><em>"AuthGroupfile"</em></tag> - <item> - <p><em>Syntax:</em> <c>AuthGroupFile</c> Filename</p> - <p><em>Default:</em> <c>none</c></p> - <p><c>AuthGroupFile</c> indicates which file that contains the list - of groups. The filename must contain the absolute path to the - file. The format of the file is one group per row and - every row contains the name of the group and the members - of the group, separated by a space, for example:</p> - <pre> -GroupName: Member1 Member2 .... MemberN</pre> - </item> - <tag><em>"AuthName"</em></tag> - <item> - <p><em>Syntax:</em> <c>AuthName</c> auth-domain</p> - <p><em>Default:</em> <c>none</c></p> - <p>Same as directive <c>AuthName</c> for the server - configuration file.</p> - </item> - <tag><em>"AuthType"</em></tag> - <item> - <p><em>Syntax:</em> <c>AuthType</c> <c>Basic</c></p> - <p><em>Default:</em> <c>Basic</c></p> - <p><c>AuthType</c> specifies which authentication scheme to - be used. Only Basic Authenticating using UUEncoding of - the password and user ID is implemented.</p> - </item> - <tag><em>"AuthUserFile"</em></tag> - <item> - <p><em>Syntax:</em> <c>AuthUserFile</c> Filename</p> - <p><em>Default:</em><c>none</c></p> - <p><c>AuthUserFile</c> indicates which file that contains the list - of users. The filename must contain the absolute path to the - file. The username and password are not encrypted so do not - place the file with users in a directory that is accessible - through the web server. The format of the file is one user per row. - Every row contains <c>UserName</c> and <c>Password</c> separated - by a colon, for example:</p> - <pre> -UserName:Password -UserName:Password</pre> - </item> - <tag><em>"deny"</em></tag> - <item> - <p><em>Syntax:</em> <c>deny</c> from subnet <c>subnet | from all</c></p> - <p><em>Context:</em> Limit</p> - <p>Same as directive <c>deny</c> for the server configuration file.</p> - </item> - <tag><em>"Limit"</em></tag> - <item> - <p><em>Syntax:</em> <c><![CDATA[<Limit]]></c> RequestMethods<c>></c></p> - <p><em>Default:</em> <c>none</c></p> - <p><c><![CDATA[<Limit>]]></c> and <c></Limit></c> are used to enclose - a group of directives applying only to requests using - the specified methods. If no request method is specified, - all request methods are verified against the restrictions.</p> - <p>Example:</p> - <pre> -<Limit POST GET HEAD> - order allow deny - require group group1 - allow from 123.145.244.5 -</Limit></pre> - </item> - <tag><em>"order"</em></tag> - <item> - <p><em>Syntax:</em> <c>order</c> <c>allow deny | deny allow</c></p> - <p><em>Default:</em> <c>allow deny</c></p> - <p><c>order</c> defines if the deny or allow control is to - be performed first.</p> - <p>If the order is set to <c>allow deny</c>, the users - network address is first controlled to be in the allow subset. - If the user network address is not in the allowed subset, the user - is denied to get the asset. If the network address is in the - allowed subset, a second control is performed. That is, - the user network address is not in the subset of network - addresses to be denied as specified by parameter <c>deny</c>.</p> - <p>If the order is set to <c>deny allow</c>, only users from networks - specified to be in the allowed subset succeeds to request - assets in the limited area.</p> - </item> - <tag><em>"require"</em></tag> - <item> - <p><em>Syntax:</em> <c>require</c> - <c>group group1 group2... | user user1 user2...</c></p> - <p><em>Default:</em> <c>none</c></p> - <p><em>Context:</em> Limit</p> - <p>For more information, see directive <c>require</c> in - <seealso marker="mod_auth">mod_auth(3)</seealso>.</p> - </item> - </taglist> - </section> - </section> - - <section> - <title>Dynamic Web Pages</title> - <marker id="dynamic_we_pages"></marker> - <p><c>Inets</c> HTTP server provides two ways of creating dynamic web + <title>Dynamic Web Pages</title> + <marker id="dynamic_we_pages"></marker> + <p><c>Inets</c> HTTP server provides two ways of creating dynamic web pages, each with its own advantages and disadvantages:</p> <taglist> <tag><em>CGI scripts</em></tag> @@ -718,23 +565,7 @@ start() -> <item><c>real_name</c> - from <seealso marker="mod_alias">mod_alias</seealso></item> </list> </section> - - <section> - <title>mod_htaccess - User Configurable Access</title> - <p>This module provides per-directory user configurable access - control.</p> - <p>Uses the following Erlang Web Server API interaction data: - </p> - <list type="bulleted"> - <item><c>real_name</c> - from <seealso marker="mod_alias">mod_alias</seealso></item> - </list> - <p>Exports the following Erlang Web Server API interaction data: - </p> - <taglist> - <tag><c>{remote_user_name, User}</c></tag> - <item>The username used for authentication.</item> - </taglist> - </section> + <section> <title>mod_log - Logging Using Text Files.</title> diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml index 987f0c3cf4..a7401f41dc 100644 --- a/lib/inets/doc/src/httpd.xml +++ b/lib/inets/doc/src/httpd.xml @@ -881,20 +881,6 @@ Transport: TLS </taglist> - <marker id="props_htaccess"></marker> - <p><em>Htaccess Authentication Properties - Requires mod_htaccess</em></p> - <taglist> - <tag><marker id="prop_access_files"></marker>{access_files, [path()]}</tag> - <item> - <p>Specifies the filenames that are used for - access files. When a request comes, every directory in the path - to the requested asset are searched after files with the - names specified by this parameter. If such a file is found, the - file is parsed and the restrictions specified in it are - applied to the request.</p> - </item> - </taglist> - <marker id="props_sec"></marker> <p><em>Security Properties - Requires mod_security</em></p> diff --git a/lib/inets/src/http_server/Makefile b/lib/inets/src/http_server/Makefile index 509a64982d..da9549406f 100644 --- a/lib/inets/src/http_server/Makefile +++ b/lib/inets/src/http_server/Makefile @@ -80,7 +80,6 @@ MODULES = \ mod_esi \ mod_get \ mod_head \ - mod_htaccess \ mod_log \ mod_range \ mod_responsecontrol \ diff --git a/lib/inets/src/http_server/mod_htaccess.erl b/lib/inets/src/http_server/mod_htaccess.erl deleted file mode 100644 index 7b742bba24..0000000000 --- a/lib/inets/src/http_server/mod_htaccess.erl +++ /dev/null @@ -1,1071 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2001-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% -%% - --module(mod_htaccess). - --export([do/1, load/2, store/2]). - --include("httpd.hrl"). --include("httpd_internal.hrl"). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Public methods that interface the eswapi %% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%---------------------------------------------------------------------- -% Public method called by the webbserver to insert the data about -% Names on accessfiles -%---------------------------------------------------------------------- -load("AccessFileName" ++ FileNames, _Context)-> - CleanFileNames=string:strip(FileNames), - {ok,[],{access_files,string:tokens(CleanFileNames," ")}}. - -store({access_files, Files} = Conf, _) when is_list(Files)-> - {ok, Conf}; -store({access_files, Value}, _) -> - {error, {wrong_type, {access_files, Value}}}. - -%---------------------------------------------------------------------- -% Public method that the webbserver calls to control the page -%---------------------------------------------------------------------- -do(Info)-> - case proplists:get_value(status, Info#mod.data) of - {_Status_code, _PhraseArgs, _Reason}-> - {proceed,Info#mod.data}; - undefined -> - control_path(Info) - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% %% -%% The functions that start the control if there is a accessfile %% -%% and if so controls if the dir is allowed or not %% -%% %% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%---------------------------------------------------------------------- -%Info = record mod as specified in httpd.hrl -%returns either {proceed,Info#mod.data} -%{proceed,[{status,403....}|Info#mod.data]} -%{proceed,[{status,401....}|Info#mod.data]} -%{proceed,[{status,500....}|Info#mod.data]} -%---------------------------------------------------------------------- -control_path(Info) -> - Path = mod_alias:path(Info#mod.data, - Info#mod.config_db, - Info#mod.request_uri), - case isErlScriptOrNotAccessibleFile(Path,Info) of - true-> - {proceed,Info#mod.data}; - false-> - case getHtAccessData(Path,Info)of - {ok,public}-> - %%There was no restrictions on the page continue - {proceed,Info#mod.data}; - {error, _Reason} -> - %%Something got wrong continue or quit??????????????????/ - {proceed,Info#mod.data}; - {accessData,AccessData}-> - controlAllowedMethod(Info,AccessData) - end - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% %% -%% These methods controls that the method the client used in the %% -%% request is one of the limited %% -%% %% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%---------------------------------------------------------------------- -%Control that if the accessmethod used is in the list of modes to challenge -% -%Info is the mod record as specified in httpd.hrl -%AccessData is an ets table whit the data in the .htaccessfiles -%---------------------------------------------------------------------- -controlAllowedMethod(Info,AccessData)-> - case allowedRequestMethod(Info,AccessData) of - allow-> - %%The request didnt use one of the limited methods - ets:delete(AccessData), - {proceed,Info#mod.data}; - challenge-> - authenticateUser(Info,AccessData) - end. - -%---------------------------------------------------------------------- -%Check the specified access method in the .htaccessfile -%---------------------------------------------------------------------- -allowedRequestMethod(Info,AccessData)-> - case ets:lookup(AccessData,limit) of - [{limit,all}]-> - challenge; - [{limit,Methods}]-> - isLimitedRequestMethod(Info,Methods) - end. - - -%---------------------------------------------------------------------- -%Check the specified accessmethods in the .htaccesfile against the users -%accessmethod -% -%Info is the record from the do call -%Methods is a list of the methods specified in the .htaccessfile -%---------------------------------------------------------------------- -isLimitedRequestMethod(Info,Methods)-> - case lists:member(Info#mod.method,Methods) of - true-> - challenge; - false -> - allow - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% %% -%% These methods controls that the user comes from an allowwed net %% -%% and if so wheather its a valid user or a challenge shall be %% -%% generated %% -%% %% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%---------------------------------------------------------------------- -%The first thing to control is that the user is from a network -%that has access to the page -%---------------------------------------------------------------------- -authenticateUser(Info,AccessData)-> - case controlNet(Info,AccessData) of - allow-> - %the network is ok control that it is an allowed user - authenticateUser2(Info,AccessData); - deny-> - %The user isnt allowed to access the pages from that network - ets:delete(AccessData), - {proceed,[{status,{403,Info#mod.request_uri, - "Restricted area not allowed from your network"}}|Info#mod.data]} - end. - - -%---------------------------------------------------------------------- -%The network the user comes from is allowed to view the resources -%control whether the user needsto supply a password or not -%---------------------------------------------------------------------- -authenticateUser2(Info,AccessData)-> - case ets:lookup(AccessData,require) of - [{require,AllowedUsers}]-> - case ets:lookup(AccessData,auth_name) of - [{auth_name,Realm}]-> - authenticateUser2(Info,AccessData,Realm,AllowedUsers); - _NoAuthName-> - ets:delete(AccessData), - {break,[{status,{500,none, - ?NICE("mod_htaccess:AuthName directive " - "not specified")}}]} - end; - [] -> - %%No special user is required the network is ok so let - %%the user in - ets:delete(AccessData), - {proceed,Info#mod.data} - end. - - -%---------------------------------------------------------------------- -%The user must send a userId and a password to get the resource -%Control if its already in the http-request -%if the file with users is bad send an 500 response -%---------------------------------------------------------------------- -authenticateUser2(Info,AccessData,Realm,AllowedUsers)-> - case authenticateUser(Info,AccessData,AllowedUsers) of - allow -> - ets:delete(AccessData), - {user,Name, _Pwd} = getAuthenticatingDataFromHeader(Info), - {proceed, [{remote_user_name,Name}|Info#mod.data]}; - challenge-> - ets:delete(AccessData), - ReasonPhrase = httpd_util:reason_phrase(401), - Message = httpd_util:message(401,none,Info#mod.config_db), - {proceed, - [{response, - {401, - ["WWW-Authenticate: Basic realm=\"",Realm, - "\"\r\n\r\n","<HTML>\n<HEAD>\n<TITLE>", - ReasonPhrase,"</TITLE>\n", - "</HEAD>\n<BODY>\n<H1>",ReasonPhrase, - "</H1>\n",Message,"\n</BODY>\n</HTML>\n"]}}| - Info#mod.data]}; - deny-> - ets:delete(AccessData), - {break,[{status,{500,none, - ?NICE("mod_htaccess:Bad path to user " - "or group file")}}]} - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% %% -%% Methods that validate the netwqork the user comes from %% -%% according to the allowed networks %% -%% %% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%--------------------------------------------------------------------- -%Controls the users networkaddress agains the specifed networks to -%allow or deny -% -%returns either allow or deny -%---------------------------------------------------------------------- -controlNet(Info,AccessData)-> - UserNetwork=getUserNetworkAddress(Info), - case getAllowDenyOrder(AccessData) of - {_deny,[],_allow,[]}-> - allow; - {deny,[],allow,AllowedNetworks}-> - controlIfAllowed(AllowedNetworks,UserNetwork,allow,deny); - {allow,AllowedNetworks,deny,[]}-> - controlIfAllowed(AllowedNetworks,UserNetwork,allow,deny); - - {deny,DeniedNetworks,allow,[]}-> - controlIfAllowed(DeniedNetworks,UserNetwork,allow,deny); - {allow,[],deny,DeniedNetworks}-> - controlIfAllowed(DeniedNetworks,UserNetwork,allow,deny); - - {deny,DeniedNetworks,allow,AllowedNetworks}-> - controlDenyAllow(DeniedNetworks,AllowedNetworks,UserNetwork); - {allow,AllowedNetworks,deny,DeniedNetworks}-> - controlAllowDeny(AllowedNetworks,DeniedNetworks,UserNetwork) - end. - - -%---------------------------------------------------------------------- -%Returns the users IP-Number -%---------------------------------------------------------------------- -getUserNetworkAddress(Info)-> - {_Socket,Address}=(Info#mod.init_data)#init_data.peername, - Address. - - -%---------------------------------------------------------------------- -%Control the users Ip-number against the ip-numbers in the .htaccessfile -%---------------------------------------------------------------------- -controlIfAllowed(AllowedNetworks,UserNetwork,IfAllowed,IfDenied)-> - case AllowedNetworks of - [{allow,all}]-> - IfAllowed; - [{deny,all}]-> - IfDenied; - [{deny,Networks}]-> - memberNetwork(Networks,UserNetwork,IfDenied,IfAllowed); - [{allow,Networks}]-> - memberNetwork(Networks,UserNetwork,IfAllowed,IfDenied); - _Error-> - IfDenied - end. - - -%---------------------------------------------------------------------% -%The Denycontrol isn't neccessary to preform since the allow control % -%override the deny control % -%---------------------------------------------------------------------% -controlDenyAllow(_DeniedNetworks, AllowedNetworks, UserNetwork)-> - case AllowedNetworks of - [{allow, all}]-> - allow; - [{allow, Networks}]-> - case memberNetwork(Networks, UserNetwork) of - true-> - allow; - false-> - deny - end - end. - - -%----------------------------------------------------------------------% -%Control that the user is in the allowed list if so control that the % -%network is in the denied list -%----------------------------------------------------------------------% -controlAllowDeny(AllowedNetworks,DeniedNetworks,UserNetwork)-> - case controlIfAllowed(AllowedNetworks,UserNetwork,allow,deny) of - allow-> - controlIfAllowed(DeniedNetworks,UserNetwork,deny,allow); - deny -> - deny - end. - -%---------------------------------------------------------------------- -%Controls if the users Ipnumber is in the list of either denied or -%allowed networks -%---------------------------------------------------------------------- -memberNetwork(Networks,UserNetwork,IfTrue,IfFalse)-> - case memberNetwork(Networks,UserNetwork) of - true-> - IfTrue; - false-> - IfFalse - end. - - -%---------------------------------------------------------------------- -%regexp match the users ip-address against the networks in the list of -%ipadresses or subnet addresses. -memberNetwork(Networks,UserNetwork)-> - case lists:filter(fun(Net)-> - case re:run(UserNetwork, - formatRegexp(Net), [{capture, first}]) of - {match,[{0,_}]}-> - true; - _NotSubNet -> - false - end - end,Networks) of - []-> - false; - _MemberNetWork -> - true - end. - - -%---------------------------------------------------------------------- -%Creates a regexp from an ip-number i.e "127.0.0-> "^127[.]0[.]0.*" -%"127.0.0.-> "^127[.]0[.]0[.].*" -%---------------------------------------------------------------------- -formatRegexp(Net)-> - [SubNet1|SubNets]=string:tokens(Net,"."), - NetRegexp=lists:foldl(fun(SubNet,Newnet)-> - Newnet ++ "[.]" ++SubNet - end,"^"++SubNet1,SubNets), - case string:len(Net)-string:rchr(Net,$.) of - 0-> - NetRegexp++"[.].*"; - _-> - NetRegexp++".*" - end. - -%---------------------------------------------------------------------- -%If the user has specified if the allow or deny check shall be preformed -%first get that order if no order is specified take -%allow - deny since its harder that deny - allow -%---------------------------------------------------------------------- -getAllowDenyOrder(AccessData)-> - case ets:lookup(AccessData,order) of - [{order,{deny,allow}}]-> - {deny,ets:lookup(AccessData,deny), - allow,ets:lookup(AccessData,allow)}; - _DefaultOrder-> - {allow,ets:lookup(AccessData,allow), - deny,ets:lookup(AccessData,deny)} - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% %% -%% The methods that validates the user %% -%% %% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%---------------------------------------------------------------------- -%Control if there is anyu autheticating data in threquest header -%if so it controls it against the users in the list Allowed Users -%---------------------------------------------------------------------- -authenticateUser(Info,AccessData,AllowedUsers)-> - case getAuthenticatingDataFromHeader(Info) of - {user,User,PassWord}-> - authenticateUser(Info,AccessData,AllowedUsers, - {user,User,PassWord}); - {error,nouser}-> - challenge; - {error, _BadData}-> - challenge - end. - - -%---------------------------------------------------------------------- -%Returns the Autheticating data in the http-request -%---------------------------------------------------------------------- -getAuthenticatingDataFromHeader(Info)-> - PrsedHeader=Info#mod.parsed_header, - case proplists:get_value("authorization", PrsedHeader) of - undefined-> - {error,nouser}; - [$B,$a,$s,$i,$c,$\ |EncodedString] = Credentials -> - case (catch base64:decode_to_string(EncodedString)) of - {'EXIT',{function_clause, _}} -> - {error, Credentials}; - UnCodedString -> - case httpd_util:split(UnCodedString,":",2) of - {ok,[User,PassWord]}-> - {user,User,PassWord}; - Other -> - {error, Other} - end - end; - BadCredentials -> - {error,BadCredentials} - end. - -%---------------------------------------------------------------------- -%Returns a list of all members of the allowed groups -%---------------------------------------------------------------------- -getGroupMembers(Groups,AllowedGroups)-> - Allowed=lists:foldl(fun({group,Name,Members},AllowedMembers)-> - case lists:member(Name,AllowedGroups) of - true-> - AllowedMembers++Members; - false -> - AllowedMembers - end - end,[],Groups), - {ok,Allowed}. - -authenticateUser(Info,AccessData,{{users,[]},{groups,Groups}},User)-> - authenticateUser(Info,AccessData,{groups,Groups},User); -authenticateUser(Info,AccessData,{{users,Users},{groups,[]}},User)-> - authenticateUser(Info,AccessData,{users,Users},User); - -authenticateUser(Info,AccessData,{{users,Users},{groups,Groups}},User)-> - AllowUser=authenticateUser(Info,AccessData,{users,Users},User), - AllowGroup=authenticateUser(Info,AccessData,{groups,Groups},User), - case {AllowGroup,AllowUser} of - {_,allow}-> - allow; - {allow,_}-> - allow; - {challenge,_}-> - challenge; - {_,challenge}-> - challenge; - {_deny,_deny}-> - deny - end; - - -%---------------------------------------------------------------------- -%Controls that the user is a member in one of the allowed group -%---------------------------------------------------------------------- -authenticateUser(Info,AccessData,{groups,AllowedGroups},{user,User,PassWord})-> - case getUsers(AccessData,group_file) of - {group_data,Groups}-> - {ok, Members } = getGroupMembers(Groups,AllowedGroups), - authenticateUser(Info,AccessData,{users,Members}, - {user,User,PassWord}); - {error, _BadData}-> - deny - end; - - -%---------------------------------------------------------------------- -%Control that the user is one of the allowed users and that the passwd is ok -%---------------------------------------------------------------------- -authenticateUser(_Info,AccessData,{users,AllowedUsers},{user,User,PassWord})-> - case lists:member(User,AllowedUsers) of - true-> - %Get the usernames and passwords from the file - case getUsers(AccessData,user_file) of - {error, _BadData}-> - deny; - {user_data,Users}-> - %Users is a list of the users in - %the userfile [{user,User,Passwd}] - checkPassWord(Users,{user,User,PassWord}) - end; - false -> - challenge - end. - - -%---------------------------------------------------------------------- -%Control that the user User={user,"UserName","PassWd"} is -%member of the list of Users -%---------------------------------------------------------------------- -checkPassWord(Users,User)-> - case lists:member(User,Users) of - true-> - allow; - false-> - challenge - end. - - -%---------------------------------------------------------------------- -%Get the users in the specified file -%UserOrGroup is an atom that specify if its a group file or a user file -%i.e. group_file or user_file -%---------------------------------------------------------------------- -getUsers({file,FileName},UserOrGroup)-> - case file:open(FileName,[read]) of - {ok,AccessFileHandle} -> - getUsers({stream,AccessFileHandle},[],UserOrGroup); - {error,Reason} -> - {error,{Reason,FileName}} - end; - - -%---------------------------------------------------------------------- -%The method that starts the lokkong for user files -%---------------------------------------------------------------------- - -getUsers(AccessData,UserOrGroup)-> - case ets:lookup(AccessData,UserOrGroup) of - [{UserOrGroup,File}]-> - getUsers({file,File},UserOrGroup); - _ -> - {error,noUsers} - end. - - -%---------------------------------------------------------------------- -%Reads data from the filehandle File to the list FileData and when its -%reach the end it returns the list in a tuple {user_file|group_file,FileData} -%---------------------------------------------------------------------- -getUsers({stream,File},FileData,UserOrGroup)-> - case io:get_line(File,[]) of - eof when UserOrGroup =:= user_file -> - {user_data,FileData}; - eof when UserOrGroup =:= group_file -> - {group_data,FileData}; - Line -> - getUsers({stream,File}, - formatUser(Line,FileData,UserOrGroup),UserOrGroup) - end. - - -%---------------------------------------------------------------------- -%If the line is a comment remove it -%---------------------------------------------------------------------- -formatUser([$#|_UserDataComment],FileData,_UserOrgroup)-> - FileData; - - -%---------------------------------------------------------------------- -%The user name in the file is Username:Passwd\n -%Remove the newline sign and split the user name in -%UserName and Password -%---------------------------------------------------------------------- -formatUser(UserData,FileData,UserOrGroup)-> - case string:tokens(UserData," \r\n")of - [User| _Whitespace] when UserOrGroup =:= user_file -> - case string:tokens(User,":") of - [Name,PassWord]-> - [{user,Name,PassWord}|FileData]; - _Error-> - FileData - end; - GroupData when UserOrGroup =:= group_file -> - parseGroupData(GroupData,FileData); - _Error -> - FileData - end. - - -%---------------------------------------------------------------------- -%if everything is right GroupData is on the form -% ["groupName:", "Member1", "Member2", "Member2" -%---------------------------------------------------------------------- -parseGroupData([GroupName|GroupData],FileData)-> - [{group,formatGroupName(GroupName),GroupData}|FileData]. - - -%---------------------------------------------------------------------- -%the line in the file is GroupName: Member1 Member2 .....MemberN -%Remove the : from the group name -%---------------------------------------------------------------------- -formatGroupName(GroupName)-> - string:strip(GroupName,right,$:). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% %% -%% Functions that parses the accessfiles %% -%% %% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%---------------------------------------------------------------------- -%Control that the asset is a real file and not a request for an virtual -%asset -%---------------------------------------------------------------------- -isErlScriptOrNotAccessibleFile(Path, _Info)-> - case file:read_file_info(Path) of - {ok,_fileInfo}-> - false; - {error,_Reason} -> - true - end. - - -%---------------------------------------------------------------------- -%Path=PathToTheRequestedFile=String -%Innfo=record#mod -%---------------------------------------------------------------------- -getHtAccessData(Path,Info)-> - HtAccessFileNames=getHtAccessFileNames(Info), - case getData(Path,Info,HtAccessFileNames) of - {ok,public}-> - {ok,public}; - {accessData,AccessData}-> - {accessData,AccessData}; - {error,Reason} -> - {error,Reason} - end. - - -%---------------------------------------------------------------------- -%returns the names of the accessfiles -%---------------------------------------------------------------------- -getHtAccessFileNames(Info)-> - case httpd_util:lookup(Info#mod.config_db,access_files) of - undefined-> - [".htaccess"]; - Files-> - Files - end. -%---------------------------------------------------------------------- -%HtAccessFileNames=["accessfileName1",..."AccessFileName2"] -%---------------------------------------------------------------------- -getData(Path,Info,HtAccessFileNames)-> - SplittedPath = re:split(Path, "/", [{return, list}]), - getData2(HtAccessFileNames,SplittedPath,Info). - -%---------------------------------------------------------------------- -%Add to together the data in the Splittedpath up to the path -%that is the alias or the document root -%Since we do not need to control after any accessfiles before here -%---------------------------------------------------------------------- -getData2(HtAccessFileNames,SplittedPath,Info)-> - case getRootPath(SplittedPath,Info) of - {error,Path}-> - {error,Path}; - {ok,StartPath,RestOfSplittedPath} -> - getData2(HtAccessFileNames,StartPath,RestOfSplittedPath,Info) - end. - - -%---------------------------------------------------------------------- -%HtAccessFilenames is a list the names the accesssfiles can have -%Path is the shortest match agains all alias and documentroot -%rest of splitted path is a list of the parts of the path -%Info is the mod recod from the server -%---------------------------------------------------------------------- -getData2(HtAccessFileNames, StartPath, RestOfSplittedPath, _Info)-> - case getHtAccessFiles(HtAccessFileNames,StartPath,RestOfSplittedPath) of - []-> - %No accessfile qiut its a public directory - {ok,public}; - Files -> - loadAccessFilesData(Files) - end. - - -%---------------------------------------------------------------------- -%Loads the data in the accessFiles specifiied by -% AccessFiles=["/hoem/public/html/accefile", -% "/home/public/html/priv/accessfile"] -%---------------------------------------------------------------------- -loadAccessFilesData(AccessFiles)-> - loadAccessFilesData(AccessFiles,ets:new(accessData,[])). - - -%---------------------------------------------------------------------- -%Returns the found data -%---------------------------------------------------------------------- -contextToValues(AccessData)-> - case ets:lookup(AccessData,context) of - [{context,Values}]-> - ets:delete(AccessData,context), - insertContext(AccessData,Values), - {accessData,AccessData}; - _Error-> - {error,errorInAccessFile} - end. - - -insertContext(_AccessData, [])-> - ok; - -insertContext(AccessData,[{allow,From}|Values])-> - insertDenyAllowContext(AccessData,{allow,From}), - insertContext(AccessData,Values); - -insertContext(AccessData,[{deny,From}|Values])-> - insertDenyAllowContext(AccessData,{deny,From}), - insertContext(AccessData,Values); - -insertContext(AccessData,[{require,{GrpOrUsr,Members}}|Values])-> - case ets:lookup(AccessData,require) of - [] when GrpOrUsr =:= users -> - ets:insert(AccessData,{require,{{users,Members},{groups,[]}}}); - - [{require,{{users,Users},{groups,Groups}}}] when GrpOrUsr =:= users -> - ets:insert(AccessData,{require,{{users,Users++Members}, - {groups,Groups}}}); - [] when GrpOrUsr =:= groups -> - ets:insert(AccessData,{require,{{users,[]},{groups,Members}}}); - - [{require,{{users,Users},{groups,Groups}}}] when GrpOrUsr =:= groups -> - ets:insert(AccessData,{require,{{users,Users}, - {groups,Groups++Members}}}) - end, - insertContext(AccessData,Values); - - - -%%limit and order directive need no transforming they areis just to insert -insertContext(AccessData,[Elem|Values])-> - ets:insert(AccessData,Elem), - insertContext(AccessData,Values). - - -insertDenyAllowContext(AccessData,{AllowDeny,From})-> - case From of - all -> - ets:insert(AccessData,{AllowDeny,all}); - _AllowedSubnets -> - case ets:lookup(AccessData,AllowDeny) of - []-> - ets:insert(AccessData,{AllowDeny,From}); - [{AllowDeny,all}]-> - ok; - [{AllowDeny,Networks}]-> - ets:insert(AccessData,{allow,Networks++From}) - end - end. - -loadAccessFilesData([],AccessData)-> - %preform context to limits - contextToValues(AccessData), - {accessData,AccessData}; - -%---------------------------------------------------------------------- -%Takes each file in the list and load the data to the ets table -%AccessData -%---------------------------------------------------------------------- -loadAccessFilesData([FileName|FileNames],AccessData)-> - case loadAccessFileData({file,FileName},AccessData) of - overRide-> - loadAccessFilesData(FileNames,AccessData); - noOverRide -> - {accessData,AccessData}; - error-> - ets:delete(AccessData), - {error,errorInAccessFile} - end. - -%---------------------------------------------------------------------- -%opens the filehandle to the specified file -%---------------------------------------------------------------------- -loadAccessFileData({file,FileName},AccessData)-> - case file:open(FileName,[read]) of - {ok,AccessFileHandle}-> - loadAccessFileData({stream,AccessFileHandle},AccessData,[]); - {error, _Reason} -> - overRide - end. - -%---------------------------------------------------------------------- -%%look att each line in the file and add them to the database -%%When end of file is reached control i overrride is allowed -%% if so return -%---------------------------------------------------------------------- -loadAccessFileData({stream,File},AccessData,FileData)-> - case io:get_line(File,[]) of - eof-> - insertData(AccessData,FileData), - case ets:match_object(AccessData,{'_',error}) of - []-> - %Case we got no error control that we can override a - %at least some of the values - case ets:match_object(AccessData, - {allow_over_ride,none}) of - []-> - overRide; - _NoOverride-> - noOverRide - end; - _ -> - error - end; - Line -> - loadAccessFileData({stream,File},AccessData, - insertLine(string:strip(Line,left),FileData)) - end. - -%---------------------------------------------------------------------- -%AccessData is a ets table where the previous found data is inserted -%FileData is a list of the directives in the last parsed file -%before insertion a control is done that the directive is allowed to -%override -%---------------------------------------------------------------------- -insertData(AccessData,{{context,Values},FileData})-> - insertData(AccessData,[{context,Values}|FileData]); - -insertData(AccessData,FileData)-> - case ets:lookup(AccessData,allow_over_ride) of - [{allow_over_ride,all}]-> - lists:foreach(fun(Elem)-> - ets:insert(AccessData,Elem) - end,FileData); - []-> - lists:foreach(fun(Elem)-> - ets:insert(AccessData,Elem) - end,FileData); - [{allow_over_ride,Directives}] when is_list(Directives)-> - lists:foreach(fun({Key,Value}) -> - case lists:member(Key,Directives) of - true-> - ok; - false -> - ets:insert(AccessData,{Key,Value}) - end - end,FileData); - [{allow_over_ride,_}]-> - %Will never appear if the user - %aint doing very strang econfig files - ok - end. -%---------------------------------------------------------------------- -%Take a line in the accessfile and transform it into a tuple that -%later can be inserted in to the ets:table -%---------------------------------------------------------------------- -%%%Here is the alternatives that resides inside the limit context - -insertLine("order"++ Order, {{context, Values}, FileData})-> - {{context,[{order,getOrder(Order)}|Values]},FileData}; -%%Let the user place a tab in the beginning -insertLine([$\t,$o,$r,$d,$e,$r|Order],{{context,Values},FileData})-> - {{context,[{order,getOrder(Order)}|Values]},FileData}; - -insertLine("allow" ++ Allow, {{context, Values}, FileData})-> - {{context,[{allow,getAllowDenyData(Allow)}|Values]},FileData}; -insertLine([$\t,$a,$l,$l,$o,$w|Allow],{{context,Values},FileData})-> - {{context,[{allow,getAllowDenyData(Allow)}|Values]},FileData}; - -insertLine("deny" ++ Deny, {{context,Values}, FileData})-> - {{context,[{deny,getAllowDenyData(Deny)}|Values]},FileData}; -insertLine([$\t, $d,$e,$n,$y|Deny],{{context,Values},FileData})-> - {{context,[{deny,getAllowDenyData(Deny)}|Values]},FileData}; - -insertLine("require" ++ Require, {{context, Values}, FileData})-> - {{context,[{require,getRequireData(Require)}|Values]},FileData}; -insertLine([$\t,$r,$e,$q,$u,$i,$r,$e|Require],{{context,Values},FileData})-> - {{context,[{require,getRequireData(Require)}|Values]},FileData}; - -insertLine("</Limit" ++ _EndLimit, {Context,FileData})-> - [Context | FileData]; -insertLine("<Limit" ++ Limit, FileData)-> - {{context,[{limit,getLimits(Limit)}]}, FileData}; - -insertLine([$A,$u,$t,$h,$U,$s,$e,$r,$F,$i,$l,$e,$\ |AuthUserFile],FileData)-> - [{user_file,string:strip(AuthUserFile,right,$\n)}|FileData]; - -insertLine([$A,$u,$t,$h,$G,$r,$o,$u,$p,$F,$i,$l,$e,$\ |AuthGroupFile], - FileData)-> - [{group_file,string:strip(AuthGroupFile,right,$\n)}|FileData]; - -insertLine("AllowOverRide" ++ AllowOverRide, FileData)-> - [{allow_over_ride,getAllowOverRideData(AllowOverRide)} - | FileData]; - -insertLine([$A,$u,$t,$h,$N,$a,$m,$e,$\ |AuthName],FileData)-> - [{auth_name,string:strip(AuthName,right,$\n)}|FileData]; - -insertLine("AuthType" ++ AuthType,FileData)-> - [{auth_type,getAuthorizationType(AuthType)}|FileData]; - -insertLine(_BadDirectiveOrComment,FileData)-> - FileData. - -%---------------------------------------------------------------------- -%transform the Data specified about override to a form that is ieasier -%handled later -%Override data="all"|"md5"|"Directive1 .... DirectioveN" -%---------------------------------------------------------------------- - -getAllowOverRideData(OverRideData)-> - case string:tokens(OverRideData," \r\n") of - ["all" ++ _] -> - all; - ["none" ++ _]-> - none; - Directives -> - getOverRideDirectives(Directives) - end. - -getOverRideDirectives(Directives)-> - lists:map(fun(Directive)-> - transformDirective(Directive) - end,Directives). -transformDirective("AuthUserFile" ++ _)-> - user_file; -transformDirective("AuthGroupFile" ++ _) -> - group_file; -transformDirective("AuthName" ++ _)-> - auth_name; -transformDirective("AuthType" ++ _)-> - auth_type; -transformDirective(_UnAllowedOverRideDirective) -> - unallowed. -%---------------------------------------------------------------------- -%Replace the string that specify which method to use for authentication -%and replace it with the atom for easier mathing -%---------------------------------------------------------------------- -getAuthorizationType(AuthType)-> - [Arg | _Crap] = string:tokens(AuthType,"\n\r\ "), - case Arg of - "Basic"-> - basic; - "MD5" -> - md5; - _What -> - error - end. -%---------------------------------------------------------------------- -%Returns a list of the specified methods to limit or the atom all -%---------------------------------------------------------------------- -getLimits(Limits)-> - case re:split(Limits,">", [{return, list}])of - [_NoEndOnLimit]-> - error; - [Methods | _Crap]-> - case re:split(Methods," ", [{return, list}]) of - [[]]-> - all; - SplittedMethods -> - SplittedMethods - end - end. - - -%---------------------------------------------------------------------- -% Transform the order to prefrom deny allow control to a tuple of atoms -%---------------------------------------------------------------------- -getOrder(Order)-> - [First | _Rest]=lists:map(fun(Part)-> - list_to_atom(Part) - end,string:tokens(Order," \n\r")), - case First of - deny-> - {deny,allow}; - allow-> - {allow,deny}; - _Error-> - error - end. - -%---------------------------------------------------------------------- -% The string AllowDeny is "from all" or "from Subnet1 Subnet2...SubnetN" -%---------------------------------------------------------------------- -getAllowDenyData(AllowDeny)-> - case string:tokens(AllowDeny," \n\r") of - [_From|AllowDenyData] when length(AllowDenyData)>=1 -> - case lists:nth(1,AllowDenyData) of - "all" -> - all; - _Hosts-> - AllowDenyData - end; - _ -> - error - end. -%---------------------------------------------------------------------- -% Fix the string that describes who is allowed to se the page -%---------------------------------------------------------------------- -getRequireData(Require)-> - [UserOrGroup|UserData]=string:tokens(Require," \n\r"), - case UserOrGroup of - "user"-> - {users,UserData}; - "group" -> - {groups,UserData}; - _Whatever -> - error - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% %% -%% Methods that collects the searchways to the accessfiles %% -%% %% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%---------------------------------------------------------------------- -% Get the whole path to the different accessfiles -%---------------------------------------------------------------------- -getHtAccessFiles(HtAccessFileNames,Path,RestOfSplittedPath)-> - getHtAccessFiles(HtAccessFileNames,Path,RestOfSplittedPath,[]). - -getHtAccessFiles(HtAccessFileNames,Path,[[]],HtAccessFiles)-> - HtAccessFiles ++ accessFilesOfPath(HtAccessFileNames,Path++"/"); - -getHtAccessFiles(_HtAccessFileNames, _Path, [], HtAccessFiles)-> - HtAccessFiles; -getHtAccessFiles(HtAccessFileNames,Path,[NextDir|RestOfSplittedPath], - AccessFiles)-> - getHtAccessFiles(HtAccessFileNames,Path++"/"++NextDir,RestOfSplittedPath, - AccessFiles ++ - accessFilesOfPath(HtAccessFileNames,Path++"/")). - - -%---------------------------------------------------------------------- -%Control if therer are any accessfies in the path -%---------------------------------------------------------------------- -accessFilesOfPath(HtAccessFileNames,Path)-> - lists:foldl(fun(HtAccessFileName,Files)-> - case file:read_file_info(Path++HtAccessFileName) of - {ok, _}-> - [Path++HtAccessFileName|Files]; - {error,_Error} -> - Files - end - end,[],HtAccessFileNames). - - -%---------------------------------------------------------------------- -%Sake the splitted path and joins it up to the documentroot or the alias -%that match first -%---------------------------------------------------------------------- - -getRootPath(SplittedPath, Info)-> - DocRoot=httpd_util:lookup(Info#mod.config_db,document_root,"/"), - PresumtiveRootPath= - [DocRoot|lists:map(fun({_Alias,RealPath})-> - RealPath - end, - httpd_util:multi_lookup(Info#mod.config_db,alias))], - getRootPath(PresumtiveRootPath,SplittedPath,Info). - - -getRootPath(PresumtiveRootPath,[[],Splittedpath],Info)-> - getRootPath(PresumtiveRootPath,["/",Splittedpath],Info); - - -getRootPath(PresumtiveRootPath,[Part,NextPart|SplittedPath],Info)-> - case lists:member(Part,PresumtiveRootPath)of - true-> - {ok,Part,[NextPart|SplittedPath]}; - false -> - getRootPath(PresumtiveRootPath, - [Part++"/"++NextPart|SplittedPath],Info) - end; - -getRootPath(PresumtiveRootPath, [Part], _Info)-> - case lists:member(Part,PresumtiveRootPath)of - true-> - {ok,Part,[]}; - false -> - {error,Part} - end. diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src index 5523d08c90..e5310fd80e 100644 --- a/lib/inets/src/inets_app/inets.app.src +++ b/lib/inets/src/inets_app/inets.app.src @@ -89,7 +89,6 @@ mod_esi, mod_get, mod_head, - mod_htaccess, mod_log, mod_range, mod_responsecontrol, diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index 1289432f0d..8622ee3eb4 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -68,8 +68,6 @@ all() -> {group, https_auth_api_dets}, {group, http_auth_api_mnesia}, {group, https_auth_api_mnesia}, - {group, http_htaccess}, - {group, https_htaccess}, {group, http_security}, {group, https_security}, {group, http_reload}, @@ -104,8 +102,6 @@ groups() -> {https_auth_api_dets, [], [{group, auth_api_dets}]}, {http_auth_api_mnesia, [], [{group, auth_api_mnesia}]}, {https_auth_api_mnesia, [], [{group, auth_api_mnesia}]}, - {http_htaccess, [], [{group, htaccess}]}, - {https_htaccess, [], [{group, htaccess}]}, {http_security, [], [{group, security}]}, {https_security, [], [{group, security}]}, {http_logging, [], [{group, logging}]}, @@ -136,7 +132,6 @@ groups() -> ]}, {auth_api_mnesia, [], [auth_api_1_1, auth_api_1_0, auth_api_0_9 ]}, - {htaccess, [], [htaccess_1_1, htaccess_1_0, htaccess_0_9]}, {security, [], [security_1_1, security_1_0]}, %% Skip 0.9 as causes timing issus in test code {logging, [], [disk_log_internal, disk_log_exists, disk_log_bad_size, disk_log_bad_file]}, @@ -262,24 +257,6 @@ init_per_group(http_0_9, Config) -> _ -> [{http_version, "HTTP/0.9"} | Config] end; -init_per_group(http_htaccess = Group, Config) -> - Path = proplists:get_value(doc_root, Config), - catch remove_htaccess(Path), - create_htaccess_data(Path, proplists:get_value(address, Config)), - ok = start_apps(Group), - init_httpd(Group, [{type, ip_comm} | Config]); -init_per_group(https_htaccess = Group, Config) -> - Path = proplists:get_value(doc_root, Config), - catch remove_htaccess(Path), - create_htaccess_data(Path, proplists:get_value(address, Config)), - catch crypto:stop(), - try crypto:start() of - ok -> - init_ssl(Group, Config) - catch - _:_ -> - {skip, "Crypto did not start"} - end; init_per_group(auth_api, Config) -> [{auth_prefix, ""} | Config]; init_per_group(auth_api_dets, Config) -> @@ -306,7 +283,6 @@ end_per_group(Group, _Config) when Group == http_basic; Group == http_auth_api; Group == http_auth_api_dets; Group == http_auth_api_mnesia; - Group == http_htaccess; Group == http_security; Group == http_reload; Group == http_post; @@ -319,7 +295,6 @@ end_per_group(Group, _Config) when Group == https_basic; Group == https_auth_api; Group == https_auth_api_dets; Group == https_auth_api_mnesia; - Group == https_htaccess; Group == https_security; Group == https_reload -> @@ -453,7 +428,8 @@ head(Config) when is_list(Config) -> Version = proplists:get_value(http_version, Config), Host = proplists:get_value(host, Config), ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host, - proplists:get_value(port, Config), proplists:get_value(node, Config), + proplists:get_value(port, Config), + proplists:get_value(node, Config), http_request("HEAD /index.html ", Version, Host), [{statuscode, head_status(Version)}, {version, Version}]). @@ -808,130 +784,6 @@ post_204(Config) -> end. %%------------------------------------------------------------------------- -htaccess_1_1(Config) when is_list(Config) -> - htaccess([{http_version, "HTTP/1.1"} | Config]). - -htaccess_1_0(Config) when is_list(Config) -> - htaccess([{http_version, "HTTP/1.0"} | Config]). - -htaccess_0_9(Config) when is_list(Config) -> - htaccess([{http_version, "HTTP/0.9"} | Config]). - -htaccess() -> - [{doc, "Test mod_auth API"}]. - -htaccess(Config) when is_list(Config) -> - Version = proplists:get_value(http_version, Config), - Host = proplists:get_value(host, Config), - Type = proplists:get_value(type, Config), - Port = proplists:get_value(port, Config), - Node = proplists:get_value(node, Config), - %% Control that authentication required! - %% Control that the pages that shall be - %% authenticated really need authenticatin - ok = httpd_test_lib:verify_request(Type, Host, Port, Node, - http_request("GET /ht/open/ ", Version, Host), - [{statuscode, 401}, - {version, Version}, - {header, "WWW-Authenticate"}]), - ok = httpd_test_lib:verify_request(Type, Host, Port, Node, - http_request("GET /ht/secret/ ", Version, Host), - [{statuscode, 401}, - {version, Version}, - {header, "WWW-Authenticate"}]), - ok = httpd_test_lib:verify_request(Type, Host, Port, Node, - http_request("GET /ht/secret/top_secret/ ", - Version, Host), - [{statuscode, 401}, - {version, Version}, - {header, "WWW-Authenticate"}]), - - %% Make sure Authenticate header is received even the second time - %% we try a incorrect password! Otherwise a browser client will hang! - ok = auth_status(auth_request("/ht/open/", - "dummy", "WrongPassword", Version, Host), Config, - [{statuscode, 401}, - {header, "WWW-Authenticate"}]), - ok = auth_status(auth_request("/ht/open/", - "dummy", "WrongPassword", Version, Host), Config, - [{statuscode, 401}, - {header, "WWW-Authenticate"}]), - - %% Control that not just the first user in the list is valid - %% Control the first user - %% Authennticating ["one:OnePassword" user first in user list] - ok = auth_status(auth_request("/ht/open/dummy.html", "one", "OnePassword", - Version, Host), Config, - [{statuscode, 200}]), - - %% Control the second user - %% Authentication OK and a directory listing is supplied! - %% ["Aladdin:open sesame" user second in user list] - ok = auth_status(auth_request("/ht/open/","Aladdin", - "AladdinPassword", Version, Host), Config, - [{statuscode, 200}]), - - %% Contro that bad passwords and userids get a good denial - %% User correct but wrong password! ["one:one" user first in user list] - ok = auth_status(auth_request("/ht/open/", "one", "one", Version, Host), Config, - [{statuscode, 401}]), - %% Neither user or password correct! ["dummy:dummy"] - ok = auth_status(auth_request("/ht/open/", "dummy", "dummy", Version, Host), Config, - [{statuscode, 401}]), - - %% Control that authetication still works, even if its a member in a group - %% Authentication OK! ["two:TwoPassword" user in first group] - ok = auth_status(auth_request("/ht/secret/dummy.html", "two", - "TwoPassword", Version, Host), Config, - [{statuscode, 200}]), - - %% Authentication OK and a directory listing is supplied! - %% ["three:ThreePassword" user in second group] - ok = auth_status(auth_request("/ht/secret/", "three", - "ThreePassword", Version, Host), Config, - [{statuscode, 200}]), - - %% Deny users with bad passwords even if the user is a group member - %% User correct but wrong password! ["two:two" user in first group] - ok = auth_status(auth_request("/ht/secret/", "two", "two", Version, Host), Config, - [{statuscode, 401}]), - %% Neither user or password correct! ["dummy:dummy"] - ok = auth_status(auth_request("/ht/secret/", "dummy", "dummy", Version, Host), Config, - [{statuscode, 401}]), - - %% control that we deny the users that are in subnet above the allowed - ok = auth_status(auth_request("/ht/blocknet/dummy.html", "four", - "FourPassword", Version, Host), Config, - [{statuscode, 403}]), - %% Control that we only applies the rules to the right methods - ok = httpd_test_lib:verify_request(Type, Host, Port, Node, - http_request("HEAD /ht/blocknet/dummy.html ", Version, Host), - [{statuscode, head_status(Version)}, - {version, Version}]), - - %% Control that the rerquire directive can be overrideen - ok = auth_status(auth_request("/ht/secret/top_secret/ ", "Aladdin", "AladdinPassword", - Version, Host), Config, - [{statuscode, 401}]), - - %% Authentication still required! - ok = httpd_test_lib:verify_request(Type, Host, Port, Node, - http_request("GET /ht/open/ ", Version, Host), - [{statuscode, 401}, - {version, Version}, - {header, "WWW-Authenticate"}]), - ok = httpd_test_lib:verify_request(Type, Host, Port, Node, - http_request("GET /ht/secret/ ", Version, Host), - [{statuscode, 401}, - {version, Version}, - {header, "WWW-Authenticate"}]), - ok = httpd_test_lib:verify_request(Type, Host, Port, Node, - http_request("GET /ht/secret/top_secret/ ", Version, Host), - [{statuscode, 401}, - {version, Version}, - {header, "WWW-Authenticate"}]). - -%%------------------------------------------------------------------------- host() -> [{doc, "Test host header"}]. @@ -2081,7 +1933,6 @@ start_apps(Group) when Group == https_basic; Group == https_auth_api; Group == https_auth_api_dets; Group == https_auth_api_mnesia; - Group == https_htaccess; Group == https_security; Group == https_reload; Group == https_not_sup; @@ -2095,7 +1946,6 @@ start_apps(Group) when Group == http_basic; Group == http_auth_api; Group == http_auth_api_dets; Group == http_auth_api_mnesia; - Group == http_htaccess; Group == http_security; Group == http_logging; Group == http_reload; @@ -2185,10 +2035,6 @@ server_config(http_auth_api_mnesia, Config) -> server_config(https_auth_api_mnesia, Config) -> ServerRoot = proplists:get_value(server_root, Config), auth_api_conf(ServerRoot, mnesia) ++ server_config(https, Config); -server_config(http_htaccess, Config) -> - auth_access_conf() ++ server_config(http, Config); -server_config(https_htaccess, Config) -> - auth_access_conf() ++ server_config(https, Config); server_config(http_security, Config) -> ServerRoot = proplists:get_value(server_root, Config), tl(auth_conf(ServerRoot)) ++ security_conf(ServerRoot) ++ server_config(http, Config); @@ -2309,8 +2155,7 @@ not_sup_conf() -> [{modules, [mod_get]}]. auth_access_conf() -> - [{modules, [mod_alias, mod_htaccess, mod_dir, mod_get, mod_head]}, - {access_files, [".htaccess"]}]. + [{modules, [mod_alias, mod_dir, mod_get, mod_head]}]. auth_conf(Root) -> [{modules, [mod_alias, mod_auth, mod_dir, mod_get, mod_head]}, @@ -2541,103 +2386,6 @@ create_range_data(Path) -> ok end. -%%% mod_htaccess -create_htaccess_data(Path, IpAddress)-> - create_htaccess_dirs(Path), - - create_html_file(filename:join([Path,"ht/open/dummy.html"])), - create_html_file(filename:join([Path,"ht/blocknet/dummy.html"])), - create_html_file(filename:join([Path,"ht/secret/dummy.html"])), - create_html_file(filename:join([Path,"ht/secret/top_secret/dummy.html"])), - - create_htaccess_file(filename:join([Path,"ht/open/.htaccess"]), - Path, "user one Aladdin"), - create_htaccess_file(filename:join([Path,"ht/secret/.htaccess"]), - Path, "group group1 group2"), - create_htaccess_file(filename:join([Path, - "ht/secret/top_secret/.htaccess"]), - Path, "user four"), - create_htaccess_file(filename:join([Path,"ht/blocknet/.htaccess"]), - Path, nouser, IpAddress), - - create_user_group_file(filename:join([Path,"ht","users.file"]), - "one:OnePassword\ntwo:TwoPassword\nthree:" - "ThreePassword\nfour:FourPassword\nAladdin:" - "AladdinPassword"), - create_user_group_file(filename:join([Path,"ht","groups.file"]), - "group1: two one\ngroup2: two three"). - -create_html_file(PathAndFileName)-> - file:write_file(PathAndFileName,list_to_binary( - "<html><head><title>test</title></head> - <body>testar</body></html>")). - -create_htaccess_file(PathAndFileName, BaseDir, RequireData)-> - file:write_file(PathAndFileName, - list_to_binary( - "AuthUserFile "++ BaseDir ++ - "/ht/users.file\nAuthGroupFile "++ BaseDir - ++ "/ht/groups.file\nAuthName Test\nAuthType" - " Basic\n<Limit>\nrequire " ++ RequireData ++ - "\n</Limit>")). - -create_htaccess_file(PathAndFileName, BaseDir, nouser, IpAddress)-> - file:write_file(PathAndFileName,list_to_binary( - "AuthUserFile "++ BaseDir ++ - "/ht/users.file\nAuthGroupFile " ++ - BaseDir ++ "/ht/groups.file\nAuthName" - " Test\nAuthType" - " Basic\n<Limit GET>\n\tallow from " ++ - format_ip(IpAddress, - string:rchr(IpAddress,$.)) ++ - "\n</Limit>")). - -create_user_group_file(PathAndFileName, Data)-> - file:write_file(PathAndFileName, list_to_binary(Data)). - -create_htaccess_dirs(Path)-> - ok = file:make_dir(filename:join([Path,"ht"])), - ok = file:make_dir(filename:join([Path,"ht/open"])), - ok = file:make_dir(filename:join([Path,"ht/blocknet"])), - ok = file:make_dir(filename:join([Path,"ht/secret"])), - ok = file:make_dir(filename:join([Path,"ht/secret/top_secret"])). - -remove_htaccess_dirs(Path)-> - file:del_dir(filename:join([Path,"ht/secret/top_secret"])), - file:del_dir(filename:join([Path,"ht/secret"])), - file:del_dir(filename:join([Path,"ht/blocknet"])), - file:del_dir(filename:join([Path,"ht/open"])), - file:del_dir(filename:join([Path,"ht"])). - -format_ip(IpAddress,Pos)when Pos > 0-> - case lists:nth(Pos,IpAddress) of - $.-> - case lists:nth(Pos-2,IpAddress) of - $.-> - format_ip(IpAddress,Pos-3); - _-> - lists:sublist(IpAddress,Pos-2) ++ "." - end; - _ -> - format_ip(IpAddress,Pos-1) - end; - -format_ip(IpAddress, _Pos)-> - "1" ++ IpAddress. - -remove_htaccess(Path)-> - file:delete(filename:join([Path,"ht/open/dummy.html"])), - file:delete(filename:join([Path,"ht/secret/dummy.html"])), - file:delete(filename:join([Path,"ht/secret/top_secret/dummy.html"])), - file:delete(filename:join([Path,"ht/blocknet/dummy.html"])), - file:delete(filename:join([Path,"ht/blocknet/.htaccess"])), - file:delete(filename:join([Path,"ht/open/.htaccess"])), - file:delete(filename:join([Path,"ht/secret/.htaccess"])), - file:delete(filename:join([Path,"ht/secret/top_secret/.htaccess"])), - file:delete(filename:join([Path,"ht","users.file"])), - file:delete(filename:join([Path,"ht","groups.file"])), - remove_htaccess_dirs(Path). - dos_hostname(Type, Port, Host, Node, Version, Max) -> TooLongHeader = lists:append(lists:duplicate(Max + 1, "a")), diff --git a/lib/inets/test/httpd_mod_SUITE.erl b/lib/inets/test/httpd_mod_SUITE.erl index 5ec4e0856d..ebef7eea6c 100644 --- a/lib/inets/test/httpd_mod_SUITE.erl +++ b/lib/inets/test/httpd_mod_SUITE.erl @@ -53,7 +53,6 @@ groups() -> {mod_actions, [], []}, {mod_security, [], []}, {mod_auth, [], []}, - {mod_htaccess, [], []}, {mod_cgi, [], []}, {mod_esi, [], []}, {mod_head, [], []}, @@ -66,7 +65,6 @@ all_version_groups ()-> {group, mod_actions}, {group, mod_security}, {group, mod_auth}, - {group, mod_htaccess}, {group, mod_cgi}, {group, mod_esi}, {group, mod_head} |