1%%--------------------------------------------------------------------
2%%
3%% %CopyrightBegin%
4%%
5%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
6%%
7%% Licensed under the Apache License, Version 2.0 (the "License");
8%% you may not use this file except in compliance with the License.
9%% You may obtain a copy of the License at
10%%
11%%     http://www.apache.org/licenses/LICENSE-2.0
12%%
13%% Unless required by applicable law or agreed to in writing, software
14%% distributed under the License is distributed on an "AS IS" BASIS,
15%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16%% See the License for the specific language governing permissions and
17%% limitations under the License.
18%%
19%% %CopyrightEnd%
20%%
21%%
22%%-----------------------------------------------------------------
23%% File: orber_acl.erl
24%%
25%% Description:
26%%    Handling ACL's (Access Control Lists).
27%%
28%% Creation date: 040723
29%%
30%%-----------------------------------------------------------------
31-module(orber_acl).
32
33-include_lib("orber/include/corba.hrl").
34-include_lib("orber/src/orber_iiop.hrl").
35
36%%-----------------------------------------------------------------
37%% External exports
38%%-----------------------------------------------------------------
39-export([init_acl/1, init_acl/2, clear_acl/0,
40	 match/2, match/3, verify/3, range/1, range/2]).
41
42%%-----------------------------------------------------------------
43%% Internal exports
44%%-----------------------------------------------------------------
45-define(ACL_DB, orber_acl_db).
46
47-define(DEBUG_LEVEL, 5).
48-define(CONTINUE, -1).
49-define(STOP, -2).
50
51-define(FORMAT(_F, _A), {error, lists:flatten(io_lib:format(_F, _A))}).
52-define(EFORMAT(_F, _A), exit(lists:flatten(io_lib:format(_F, _A)))).
53
54%%-----------------------------------------------------------------
55%% Record Definitions
56%%-----------------------------------------------------------------
57-record(acl, {key, bits = ?CONTINUE, mask, interfaces = [],
58	      ports = 0, flags = 0}).
59
60%%-----------------------------------------------------------------
61%% External functions
62%%-----------------------------------------------------------------
63
64%%-----------------------------------------------------------------
65%% function : verify/1
66%% Arguments: IP - string()
67%%            Filter - string() see init_acl
68%%            Family - inet | inet6
69%% Returns  : boolean()
70%% Exception:
71%% Effect   :
72%%-----------------------------------------------------------------
73verify(IP, Filter, Family) ->
74    DB = ets:new(orber_temporary_acl_table_created_by_user,
75		 [set, public, {keypos, 2}]),
76    Result =
77	case catch verify_helper(IP, Filter, Family, DB) of
78	    true ->
79		true;
80	    {ok, Low, High} ->
81		{false, Low, High};
82	    What ->
83		{error, ?FORMAT("Uknown Error: ~p\n", [What])}
84	end,
85    ets:delete(DB),
86    Result.
87
88verify_helper(IP, Filter, Family, DB) ->
89    init_acl([{tcp_in, Filter}], Family, DB),
90    {ok, IPTuple} = inet:getaddr(IP, Family),
91    case match_helper(tuple_to_list(IPTuple), tcp_in, DB, false, tcp_in) of
92	true ->
93	    true;
94	false ->
95	    range(Filter, Family)
96    end.
97
98%%-----------------------------------------------------------------
99%% function : range/1/2
100%% Arguments: Filter - string(). See init_acl
101%%            Family - inet | inet6
102%% Returns  : {ok, From, To}
103%%            From - To - string()
104%% Exception:
105%% Effect   :
106%%-----------------------------------------------------------------
107range(Filter) ->
108    range(Filter, inet).
109
110range(Filter, inet) ->
111    range_safe(Filter, inet, ".", 16#FF, "255", 8, "~p.", "~p", 3);
112range(Filter, Family) ->
113    range_safe(Filter, Family, ":", 16#FFFF, "FFFF", 16, "~.16B:", "~.16B", 7).
114
115range_safe(Filter, Family, Separator, Max, MaxStr, N, F1, F2, X) ->
116    case catch range_helper(Filter, Family, Separator, Max, MaxStr, N, F1, F2, X) of
117	{ok, Low, High} ->
118	    {ok, Low, High};
119	{'EXIT',{format,Why}} ->
120	    {error, ?FORMAT("Unable to format string: ~p\n", [Why])};
121	{'EXIT', E} ->
122	    {error, ?FORMAT("Exit: ~p\n", [E])};
123	What ->
124	    {error, ?FORMAT("Unknown Error: ~p\n", [What])}
125    end.
126
127range_helper(Filter, Family, Separator, Max, MaxStr, N, F1, F2, X) ->
128    {MaskStr, Bits, _Ports} = tokenize(Filter, Family),
129    {ok, MaskTuple} = inet:getaddr(MaskStr, Family),
130    NoOfFull = Bits div N,
131    Mask = get_mask(N, (Bits rem N)),
132    case split(NoOfFull, tuple_to_list(MaskTuple)) of
133	{Full, [Partial|_DontCare]} ->
134	    Beginning = pp(Full, [], F1),
135	    MiddleLow = io_lib:format(F2, [(Mask band Partial) + ((Mask bxor Max) band 0)]),
136	    MiddleHigh = io_lib:format(F2, [(Mask band Partial) + ((Mask bxor Max) band Max)]),
137	    EndLow = lists:duplicate((X-NoOfFull), Separator ++ "0"),
138	    EndHigh = lists:duplicate((X-NoOfFull), Separator ++ MaxStr),
139	    Low = lists:flatten([Beginning, MiddleLow, EndLow]),
140	    High = lists:flatten([Beginning, MiddleHigh, EndHigh]),
141	    {ok, Low, High};
142	{Full, []} ->
143	    Address = lists:flatten(pp(Full, [], F1)),
144	    {ok, Address, Address}
145    end.
146
147pp([], Acc, _) ->
148    Acc;
149pp([H|T], Acc, Format) ->
150    pp(T, Acc ++ io_lib:format(Format, [H]), Format).
151
152split(N, List) when is_integer(N) andalso N >= 0 andalso is_list(List) ->
153    case split(N, List, []) of
154        Fault when is_atom(Fault) ->
155            erlang:error(Fault, [N,List]);
156        Result ->
157            Result
158    end;
159split(N, List) ->
160    erlang:error(badarg, [N,List]).
161
162split(0, L, R) ->
163    {lists:reverse(R, []), L};
164split(N, [H|T], R) ->
165    split(N-1, T, [H|R]);
166split(_, [], _) ->
167    badarg.
168
169
170%%-----------------------------------------------------------------
171%% function : clear_acl/0
172%% Arguments: -
173%% Returns  :
174%% Exception:
175%% Effect   :
176%%-----------------------------------------------------------------
177clear_acl() ->
178    clear_acl(?ACL_DB).
179clear_acl(DB) ->
180    (catch ets:delete(DB)),
181    ok.
182
183%%-----------------------------------------------------------------
184%% function : init_acl/1/2
185%% Arguments: Filters - [{Direction, Filter}] | [{Direction, Filter, [Interfaces]}]
186%%            Direction - tcp_in | ssl_in | tcp_out | ssl_out
187%%            Filter - string(). Examples:
188%%     * "123.456.789.10" - match against all bits.
189%%     * "123.456.789.10/17" - match against the 17 most significant bits.
190%%     * "123.456.789.10/17#4001" - as above but only allow port 4001
191%%     * "123.456.789.10/17#4001/5001" - as above but only allow port 4001-5001
192%%            Family - inet | inet6
193%% Returns  : ok | {'EXCEPTION', E}
194%% Exception: 'BAD_PARAM'
195%% Effect   :
196%%-----------------------------------------------------------------
197init_acl(Filters) ->
198    DB = ets:new(?ACL_DB, [set, public, named_table, {keypos, 2}]),
199    case ?ORB_FLAG_TEST(orber:get_flags(), ?ORB_ENV_USE_IPV6) of
200	false ->
201	    init_acl(Filters, inet, DB);
202	true ->
203	    init_acl(Filters, inet6, DB)
204    end.
205
206init_acl(Filters, Family) ->
207    DB = ets:new(?ACL_DB, [set, public, named_table, {keypos, 2}]),
208    init_acl(Filters, Family, DB).
209
210init_acl([], _, DB) ->
211    {ok, DB};
212init_acl([Data|T], Family, DB) ->
213    {Direction, Filter, Interfaces} =
214	case Data of
215	    {D, F, I} ->
216		{D, F, I};
217	    {D, F} ->
218		{D, F, []}
219	end,
220    {MaskStr, Bits, Ports} = tokenize(Filter, Family),
221    case inet:getaddr(MaskStr, Family) of
222	{ok, Addr} when size(Addr) == 4 ->
223	    create_mask(tuple_to_list(Addr), Bits div 8,
224			get_mask8((Bits rem 8)), DB, Direction, Interfaces, Ports),
225	    init_acl(T, Family, DB);
226	{ok, Addr} ->
227	    create_mask(tuple_to_list(Addr), Bits div 16,
228			get_mask16((Bits rem 16)), DB, Direction, Interfaces, Ports),
229	    init_acl(T, Family, DB)
230    end.
231
232create_mask(List, Div, Mask, DB, Direction, Interfaces, Ports) ->
233    case split(Div, List) of
234	{[], [Partial|_DontCare]} ->
235	    %% Less than 8/16 bits (depends on family).
236	    add_parts([], Direction, (Partial band Mask), Mask, DB,
237		      Interfaces, Ports);
238	{Full, [Partial|_DontCare]} ->
239	    add_parts(Full, Direction, (Partial band Mask), Mask, DB,
240		      Interfaces, Ports);
241	{Full, []} ->
242	    %% 32 bits.
243	    add_parts(Full, Direction, ?STOP, Mask, DB, Interfaces, Ports)
244    end.
245
246add_parts([], Parent, Bits, Mask, DB, Interfaces, Ports) ->
247    ets:insert(DB, #acl{key = Parent, bits = Bits,
248			mask = Mask, interfaces = Interfaces, ports = Ports});
249add_parts([H|T], Parent, Bits, Mask, DB, Interfaces, Ports) ->
250    Key = {Parent, H},
251    ets:insert(DB, #acl{key = Key}),
252    add_parts(T, Key, Bits, Mask, DB, Interfaces, Ports).
253
254
255%%-----------------------------------------------------------------
256%% function : match/1/2
257%% Arguments: IP - tuple() | [integer()]
258%%            Direction - tcp_in | ssl_in | tcp_out | ssl_out
259%%            All - boolean()
260%% Returns  :
261%% Exception:
262%% Effect   :
263%%-----------------------------------------------------------------
264match(IPTuple, Direction) when is_tuple(IPTuple) ->
265    match_helper(tuple_to_list(IPTuple), Direction, ?ACL_DB, false, Direction);
266match(IPList, Direction) ->
267    match_helper(IPList, Direction, ?ACL_DB, false, Direction).
268
269match(IPTuple, Direction, All) when is_tuple(IPTuple) ->
270    match_helper(tuple_to_list(IPTuple), Direction, ?ACL_DB, All, Direction);
271match(IPList, Direction, All) ->
272    match_helper(IPList, Direction, ?ACL_DB, All, Direction).
273
274match_helper([], _, _, false, _) -> false;
275match_helper([], _, _, true, _) -> {false, [], 0};
276match_helper([H|T], Parent, DB, All, Direction) ->
277    case ets:lookup(DB, {Parent, H}) of
278	[#acl{bits = ?CONTINUE}] ->
279	    match_helper(T, {Parent, H}, DB, All, Direction);
280	[#acl{bits = ?STOP}] when All == false ->
281	    true;
282	[#acl{bits = ?STOP, interfaces = I, ports = Ports}] ->
283	    {true, I, Ports};
284	[#acl{bits = Bits, mask = Mask}] when All == false ->
285	    Bits == (hd(T) band Mask);
286	[#acl{bits = Bits, mask = Mask, interfaces = I, ports = Ports}] ->
287	    {Bits == (hd(T) band Mask), I, Ports};
288	_ ->
289	    %% Less than 8/16 significant bits (depends on family).
290	    %% Should we even allow this?
291	    case ets:lookup(DB, Direction) of
292		[#acl{bits = Bits, mask = Mask}] when is_integer(Bits) andalso
293						      All == false  ->
294		    Bits == (H band Mask);
295		[#acl{bits = Bits, mask = Mask,
296		      interfaces = I, ports = Ports}] when is_integer(Bits) ->
297		    {Bits == (H band Mask), I, Ports};
298		_ when All == false ->
299		    false;
300		_ ->
301		    {false, [], 0}
302	    end
303    end.
304
305
306%%-----------------------------------------------------------------
307%% Internal functions
308%%-----------------------------------------------------------------
309%%-----------------------------------------------------------------
310%% function : tokenize/1
311%% Arguments: Filter - string(). Examples:
312%%     * "123.456.789.10" - match against all.
313%%     * "123.456.789.10/17" - match against the 17 most significant bits.
314%%     * "123.456.789.10/17#4001" - as above but only allow port 4001
315%%     * "123.456.789.10/17#4001/5001" - as above but only allow port 4001-5001
316%%            Family - inet | inet6
317%% Returns  : {MaskStr, Bits, Ports}
318%%            MaskStr - string()
319%%            Bits    - integer()
320%%            Ports   - integer() | {integer(), integer()}
321%% Exception:
322%% Effect   :
323%%-----------------------------------------------------------------
324tokenize(Filter, Family) ->
325    case string:tokens(Filter, "/#") of
326	[MaskStr] when Family == inet ->
327	    {MaskStr, 32, 0};
328	[MaskStr] when Family == inet6 ->
329	    {MaskStr, 128, 0};
330	[MaskStr, BitString] ->
331	    {MaskStr, list_to_integer(BitString), 0};
332	[MaskStr, BitString, Port] ->
333	    {MaskStr, list_to_integer(BitString), list_to_integer(Port)};
334	[MaskStr, BitString, MinPort, MaxPort] ->
335	    {MaskStr, list_to_integer(BitString),
336	     {list_to_integer(MinPort), list_to_integer(MaxPort)}};
337	What ->
338	    ?EFORMAT("Invalid Filter: ~p\nReason: ~p\n", [Filter, What])
339    end.
340
341
342%%-----------------------------------------------------------------
343%% function : get_mask/2
344%% Arguments:
345%% Returns  :
346%% Exception:
347%% Effect   :
348%%-----------------------------------------------------------------
349get_mask(8, Bits) ->
350    get_mask8(Bits);
351get_mask(16, Bits) ->
352    get_mask16(Bits).
353
354%%-----------------------------------------------------------------
355%% function : get_mask8/1
356%% Arguments:
357%% Returns  :
358%% Exception:
359%% Effect   :
360%%-----------------------------------------------------------------
361get_mask8(0) -> 2#00000000;
362get_mask8(1) -> 2#10000000;
363get_mask8(2) -> 2#11000000;
364get_mask8(3) -> 2#11100000;
365get_mask8(4) -> 2#11110000;
366get_mask8(5) -> 2#11111000;
367get_mask8(6) -> 2#11111100;
368get_mask8(7) -> 2#11111110.
369
370%%-----------------------------------------------------------------
371%% function : get_mask16/1
372%% Arguments:
373%% Returns  :
374%% Exception:
375%% Effect   :
376%%-----------------------------------------------------------------
377get_mask16(0)  -> 2#0000000000000000;
378get_mask16(1)  -> 2#1000000000000000;
379get_mask16(2)  -> 2#1100000000000000;
380get_mask16(3)  -> 2#1110000000000000;
381get_mask16(4)  -> 2#1111000000000000;
382get_mask16(5)  -> 2#1111100000000000;
383get_mask16(6)  -> 2#1111110000000000;
384get_mask16(7)  -> 2#1111111000000000;
385get_mask16(8)  -> 2#1111111100000000;
386get_mask16(9)  -> 2#1111111110000000;
387get_mask16(10) -> 2#1111111111000000;
388get_mask16(11) -> 2#1111111111100000;
389get_mask16(12) -> 2#1111111111110000;
390get_mask16(13) -> 2#1111111111111000;
391get_mask16(14) -> 2#1111111111111100;
392get_mask16(15) -> 2#1111111111111110.
393
394
395%%-----------------------------------------------------------------
396%%------------- END OF MODULE -------------------------------------
397%%-----------------------------------------------------------------
398