diff options
Diffstat (limited to 'lib/orber/src/orber_acl.erl')
-rw-r--r-- | lib/orber/src/orber_acl.erl | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/lib/orber/src/orber_acl.erl b/lib/orber/src/orber_acl.erl new file mode 100644 index 0000000000..5c696a9b2a --- /dev/null +++ b/lib/orber/src/orber_acl.erl @@ -0,0 +1,396 @@ +%%-------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%----------------------------------------------------------------- +%% File: orber_acl.erl +%% +%% Description: +%% Handling ACL's (Access Control Lists). +%% +%% Creation date: 040723 +%% +%%----------------------------------------------------------------- +-module(orber_acl). + +-include_lib("orber/include/corba.hrl"). +-include_lib("orber/src/orber_iiop.hrl"). + +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([init_acl/1, init_acl/2, clear_acl/0, + match/2, match/3, verify/3, range/1, range/2]). + +%%----------------------------------------------------------------- +%% Internal exports +%%----------------------------------------------------------------- +-define(ACL_DB, orber_acl_db). + +-define(DEBUG_LEVEL, 5). +-define(CONTINUE, -1). +-define(STOP, -2). + +-define(FORMAT(_F, _A), {error, lists:flatten(io_lib:format(_F, _A))}). +-define(EFORMAT(_F, _A), exit(lists:flatten(io_lib:format(_F, _A)))). + +%%----------------------------------------------------------------- +%% Record Definitions +%%----------------------------------------------------------------- +-record(acl, {key, bits = ?CONTINUE, mask, interfaces = [], + ports = 0, flags = 0}). + +%%----------------------------------------------------------------- +%% External functions +%%----------------------------------------------------------------- + +%%----------------------------------------------------------------- +%% function : verify/1 +%% Arguments: IP - string() +%% Filter - string() see init_acl +%% Family - inet | inet6 +%% Returns : boolean() +%% Exception: +%% Effect : +%%----------------------------------------------------------------- +verify(IP, Filter, Family) -> + DB = ets:new(orber_temporary_acl_table_created_by_user, + [set, public, {keypos, 2}]), + Result = + case catch verify_helper(IP, Filter, Family, DB) of + true -> + true; + {ok, Low, High} -> + {false, Low, High}; + What -> + {error, ?FORMAT("Uknown Error: ~p\n", [What])} + end, + ets:delete(DB), + Result. + +verify_helper(IP, Filter, Family, DB) -> + init_acl([{tcp_in, Filter}], Family, DB), + {ok, IPTuple} = inet:getaddr(IP, Family), + case match_helper(tuple_to_list(IPTuple), tcp_in, DB, false, tcp_in) of + true -> + true; + false -> + range(Filter, Family) + end. + +%%----------------------------------------------------------------- +%% function : range/1/2 +%% Arguments: Filter - string(). See init_acl +%% Family - inet | inet6 +%% Returns : {ok, From, To} +%% From - To - string() +%% Exception: +%% Effect : +%%----------------------------------------------------------------- +range(Filter) -> + range(Filter, inet). + +range(Filter, inet) -> + range_safe(Filter, inet, ".", 16#FF, "255", 8, "~p.", "~p", 3); +range(Filter, Family) -> + range_safe(Filter, Family, ":", 16#FFFF, "FFFF", 16, "~.16B:", "~.16B", 7). + +range_safe(Filter, Family, Separator, Max, MaxStr, N, F1, F2, X) -> + case catch range_helper(Filter, Family, Separator, Max, MaxStr, N, F1, F2, X) of + {ok, Low, High} -> + {ok, Low, High}; + {'EXIT',{format,Why}} -> + {error, ?FORMAT("Unable to format string: ~p\n", [Why])}; + {'EXIT', E} -> + {error, ?FORMAT("Exit: ~p\n", [E])}; + What -> + {error, ?FORMAT("Unknown Error: ~p\n", [What])} + end. + +range_helper(Filter, Family, Separator, Max, MaxStr, N, F1, F2, X) -> + {MaskStr, Bits, _Ports} = tokenize(Filter, Family), + {ok, MaskTuple} = inet:getaddr(MaskStr, Family), + NoOfFull = Bits div N, + Mask = get_mask(N, (Bits rem N)), + case split(NoOfFull, tuple_to_list(MaskTuple)) of + {Full, [Partial|_DontCare]} -> + Beginning = pp(Full, [], F1), + MiddleLow = io_lib:format(F2, [(Mask band Partial) + ((Mask bxor Max) band 0)]), + MiddleHigh = io_lib:format(F2, [(Mask band Partial) + ((Mask bxor Max) band Max)]), + EndLow = lists:duplicate((X-NoOfFull), Separator ++ "0"), + EndHigh = lists:duplicate((X-NoOfFull), Separator ++ MaxStr), + Low = lists:flatten([Beginning, MiddleLow, EndLow]), + High = lists:flatten([Beginning, MiddleHigh, EndHigh]), + {ok, Low, High}; + {Full, []} -> + Address = lists:flatten(pp(Full, [], F1)), + {ok, Address, Address} + end. + +pp([], Acc, _) -> + Acc; +pp([H|T], Acc, Format) -> + pp(T, Acc ++ io_lib:format(Format, [H]), Format). + +split(N, List) when is_integer(N) andalso N >= 0 andalso is_list(List) -> + case split(N, List, []) of + Fault when is_atom(Fault) -> + erlang:error(Fault, [N,List]); + Result -> + Result + end; +split(N, List) -> + erlang:error(badarg, [N,List]). + +split(0, L, R) -> + {lists:reverse(R, []), L}; +split(N, [H|T], R) -> + split(N-1, T, [H|R]); +split(_, [], _) -> + badarg. + + +%%----------------------------------------------------------------- +%% function : clear_acl/0 +%% Arguments: - +%% Returns : +%% Exception: +%% Effect : +%%----------------------------------------------------------------- +clear_acl() -> + clear_acl(?ACL_DB). +clear_acl(DB) -> + (catch ets:delete(DB)), + ok. + +%%----------------------------------------------------------------- +%% function : init_acl/1/2 +%% Arguments: Filters - [{Direction, Filter}] | [{Direction, Filter, [Interfaces]}] +%% Direction - tcp_in | ssl_in | tcp_out | ssl_out +%% Filter - string(). Examples: +%% * "123.456.789.10" - match against all bits. +%% * "123.456.789.10/17" - match against the 17 most significant bits. +%% * "123.456.789.10/17#4001" - as above but only allow port 4001 +%% * "123.456.789.10/17#4001/5001" - as above but only allow port 4001-5001 +%% Family - inet | inet6 +%% Returns : ok | {'EXCEPTION', E} +%% Exception: 'BAD_PARAM' +%% Effect : +%%----------------------------------------------------------------- +init_acl(Filters) -> + DB = ets:new(?ACL_DB, [set, public, named_table, {keypos, 2}]), + case ?ORB_FLAG_TEST(orber:get_flags(), ?ORB_ENV_USE_IPV6) of + false -> + init_acl(Filters, inet, DB); + true -> + init_acl(Filters, inet6, DB) + end. + +init_acl(Filters, Family) -> + DB = ets:new(?ACL_DB, [set, public, named_table, {keypos, 2}]), + init_acl(Filters, Family, DB). + +init_acl([], _, DB) -> + {ok, DB}; +init_acl([Data|T], Family, DB) -> + {Direction, Filter, Interfaces} = + case Data of + {D, F, I} -> + {D, F, I}; + {D, F} -> + {D, F, []} + end, + {MaskStr, Bits, Ports} = tokenize(Filter, Family), + case inet:getaddr(MaskStr, Family) of + {ok, Addr} when size(Addr) == 4 -> + create_mask(tuple_to_list(Addr), Bits div 8, + get_mask8((Bits rem 8)), DB, Direction, Interfaces, Ports), + init_acl(T, Family, DB); + {ok, Addr} -> + create_mask(tuple_to_list(Addr), Bits div 16, + get_mask16((Bits rem 16)), DB, Direction, Interfaces, Ports), + init_acl(T, Family, DB) + end. + +create_mask(List, Div, Mask, DB, Direction, Interfaces, Ports) -> + case split(Div, List) of + {[], [Partial|_DontCare]} -> + %% Less than 8/16 bits (depends on family). + add_parts([], Direction, (Partial band Mask), Mask, DB, + Interfaces, Ports); + {Full, [Partial|_DontCare]} -> + add_parts(Full, Direction, (Partial band Mask), Mask, DB, + Interfaces, Ports); + {Full, []} -> + %% 32 bits. + add_parts(Full, Direction, ?STOP, Mask, DB, Interfaces, Ports) + end. + +add_parts([], Parent, Bits, Mask, DB, Interfaces, Ports) -> + ets:insert(DB, #acl{key = Parent, bits = Bits, + mask = Mask, interfaces = Interfaces, ports = Ports}); +add_parts([H|T], Parent, Bits, Mask, DB, Interfaces, Ports) -> + Key = {Parent, H}, + ets:insert(DB, #acl{key = Key}), + add_parts(T, Key, Bits, Mask, DB, Interfaces, Ports). + + +%%----------------------------------------------------------------- +%% function : match/1/2 +%% Arguments: IP - tuple() | [integer()] +%% Direction - tcp_in | ssl_in | tcp_out | ssl_out +%% All - boolean() +%% Returns : +%% Exception: +%% Effect : +%%----------------------------------------------------------------- +match(IPTuple, Direction) when is_tuple(IPTuple) -> + match_helper(tuple_to_list(IPTuple), Direction, ?ACL_DB, false, Direction); +match(IPList, Direction) -> + match_helper(IPList, Direction, ?ACL_DB, false, Direction). + +match(IPTuple, Direction, All) when is_tuple(IPTuple) -> + match_helper(tuple_to_list(IPTuple), Direction, ?ACL_DB, All, Direction); +match(IPList, Direction, All) -> + match_helper(IPList, Direction, ?ACL_DB, All, Direction). + +match_helper([], _, _, false, _) -> false; +match_helper([], _, _, true, _) -> {false, [], 0}; +match_helper([H|T], Parent, DB, All, Direction) -> + case ets:lookup(DB, {Parent, H}) of + [#acl{bits = ?CONTINUE}] -> + match_helper(T, {Parent, H}, DB, All, Direction); + [#acl{bits = ?STOP}] when All == false -> + true; + [#acl{bits = ?STOP, interfaces = I, ports = Ports}] -> + {true, I, Ports}; + [#acl{bits = Bits, mask = Mask}] when All == false -> + Bits == (hd(T) band Mask); + [#acl{bits = Bits, mask = Mask, interfaces = I, ports = Ports}] -> + {Bits == (hd(T) band Mask), I, Ports}; + _ -> + %% Less than 8/16 significant bits (depends on family). + %% Should we even allow this? + case ets:lookup(DB, Direction) of + [#acl{bits = Bits, mask = Mask}] when is_integer(Bits) andalso + All == false -> + Bits == (H band Mask); + [#acl{bits = Bits, mask = Mask, + interfaces = I, ports = Ports}] when is_integer(Bits) -> + {Bits == (H band Mask), I, Ports}; + _ when All == false -> + false; + _ -> + {false, [], 0} + end + end. + + +%%----------------------------------------------------------------- +%% Internal functions +%%----------------------------------------------------------------- +%%----------------------------------------------------------------- +%% function : tokenize/1 +%% Arguments: Filter - string(). Examples: +%% * "123.456.789.10" - match against all. +%% * "123.456.789.10/17" - match against the 17 most significant bits. +%% * "123.456.789.10/17#4001" - as above but only allow port 4001 +%% * "123.456.789.10/17#4001/5001" - as above but only allow port 4001-5001 +%% Family - inet | inet6 +%% Returns : {MaskStr, Bits, Ports} +%% MaskStr - string() +%% Bits - integer() +%% Ports - integer() | {integer(), integer()} +%% Exception: +%% Effect : +%%----------------------------------------------------------------- +tokenize(Filter, Family) -> + case string:tokens(Filter, "/#") of + [MaskStr] when Family == inet -> + {MaskStr, 32, 0}; + [MaskStr] when Family == inet6 -> + {MaskStr, 128, 0}; + [MaskStr, BitString] -> + {MaskStr, list_to_integer(BitString), 0}; + [MaskStr, BitString, Port] -> + {MaskStr, list_to_integer(BitString), list_to_integer(Port)}; + [MaskStr, BitString, MinPort, MaxPort] -> + {MaskStr, list_to_integer(BitString), + {list_to_integer(MinPort), list_to_integer(MaxPort)}}; + What -> + ?EFORMAT("Invalid Filter: ~p\nReason: ~p\n", [Filter, What]) + end. + + +%%----------------------------------------------------------------- +%% function : get_mask/2 +%% Arguments: +%% Returns : +%% Exception: +%% Effect : +%%----------------------------------------------------------------- +get_mask(8, Bits) -> + get_mask8(Bits); +get_mask(16, Bits) -> + get_mask16(Bits). + +%%----------------------------------------------------------------- +%% function : get_mask8/1 +%% Arguments: +%% Returns : +%% Exception: +%% Effect : +%%----------------------------------------------------------------- +get_mask8(0) -> 2#00000000; +get_mask8(1) -> 2#10000000; +get_mask8(2) -> 2#11000000; +get_mask8(3) -> 2#11100000; +get_mask8(4) -> 2#11110000; +get_mask8(5) -> 2#11111000; +get_mask8(6) -> 2#11111100; +get_mask8(7) -> 2#11111110. + +%%----------------------------------------------------------------- +%% function : get_mask16/1 +%% Arguments: +%% Returns : +%% Exception: +%% Effect : +%%----------------------------------------------------------------- +get_mask16(0) -> 2#0000000000000000; +get_mask16(1) -> 2#1000000000000000; +get_mask16(2) -> 2#1100000000000000; +get_mask16(3) -> 2#1110000000000000; +get_mask16(4) -> 2#1111000000000000; +get_mask16(5) -> 2#1111100000000000; +get_mask16(6) -> 2#1111110000000000; +get_mask16(7) -> 2#1111111000000000; +get_mask16(8) -> 2#1111111100000000; +get_mask16(9) -> 2#1111111110000000; +get_mask16(10) -> 2#1111111111000000; +get_mask16(11) -> 2#1111111111100000; +get_mask16(12) -> 2#1111111111110000; +get_mask16(13) -> 2#1111111111111000; +get_mask16(14) -> 2#1111111111111100; +get_mask16(15) -> 2#1111111111111110. + + +%%----------------------------------------------------------------- +%%------------- END OF MODULE ------------------------------------- +%%----------------------------------------------------------------- |