1
%% ``The contents of this file are subject to the Erlang Public License,
2
%% Version 1.1, (the "License"); you may not use this file except in
3
%% compliance with the License. You should have received a copy of the
4
%% Erlang Public License along with this software. If not, it can be
5
%% retrieved via the world wide web at http://www.erlang.org/.
7
%% Software distributed under the License is distributed on an "AS IS"
8
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9
%% the License for the specific language governing rights and limitations
12
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
13
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
14
%% AB. All Rights Reserved.''
20
-export([get_mib_view/5]).
21
-export([init/1, init/2, backup/1]).
22
-export([delete/1, get_row/1, get_next_row/1, insert/1, insert/2,
25
-include("SNMPv2-TC.hrl").
26
-include("SNMP-VIEW-BASED-ACM-MIB.hrl").
27
-include("SNMP-FRAMEWORK-MIB.hrl").
28
-include("snmp_types.hrl").
29
-include("snmpa_vacm.hrl").
31
-define(VMODULE,"VACM").
32
-include("snmp_verbosity.hrl").
35
%%%-----------------------------------------------------------------
36
%%% Access Control Module for VACM (see also snmpa_acm)
37
%%% This module implements:
38
%%% 1. access control functions for VACM
39
%%% 2. vacmAccessTable as an ordered ets table
41
%%% This version of VACM handles v1, v2c and v3.
42
%%%-----------------------------------------------------------------
44
%%%-----------------------------------------------------------------
45
%%% 1. access control functions for VACM
46
%%%-----------------------------------------------------------------
47
%%-----------------------------------------------------------------
48
%% Func: get_mib_view/5 -> {ok, ViewName} |
49
%% {discarded, Reason}
50
%% Types: ViewType = read | write | notify
51
%% SecModel = ?SEC_* (see snmp_types.hrl)
53
%% SecLevel = ?'SnmpSecurityLevel_*' (see SNMP-FRAMEWORK-MIB.hrl)
54
%% ContextName = string()
55
%% Purpose: This function is used to map VACM parameters to a mib
57
%%-----------------------------------------------------------------
58
get_mib_view(ViewType, SecModel, SecName, SecLevel, ContextName) ->
59
check_auth(catch auth(ViewType, SecModel, SecName, SecLevel, ContextName)).
62
%% Follows the procedure in rfc2275
63
auth(ViewType, SecModel, SecName, SecLevel, ContextName) ->
64
% 3.2.1 - Check that the context is known to us
65
?vdebug("check that the context (~p) is known to us",[ContextName]),
66
case snmp_view_based_acm_mib:vacmContextTable(get, ContextName,
67
[?vacmContextName]) of
71
snmpa_mpd:inc(snmpUnknownContexts),
72
throw({discarded, noSuchContext})
74
% 3.2.2 - Check that the SecModel and SecName is valid
75
?vdebug("check that SecModel (~p) and SecName (~p) is valid",
78
case snmp_view_based_acm_mib:get(vacmSecurityToGroupTable,
79
[SecModel, length(SecName) | SecName],
80
[?vacmGroupName, ?vacmSecurityToGroupStatus]) of
81
[{value, GN}, {value, ?'RowStatus_active'}] ->
83
[{value, _GN}, {value, RowStatus}] ->
84
?vlog("valid SecModel and SecName but wrong row status:"
85
"~n RowStatus: ~p", [RowStatus]),
86
throw({discarded, noGroupName});
88
throw({discarded, noGroupName})
90
% 3.2.3-4 - Find an access entry and its view name
91
?vdebug("find an access entry and its view name",[]),
93
case get_view_name(ViewType, GroupName, ContextName,
94
SecModel, SecLevel) of
98
% 3.2.5a - Find the corresponding mib view
99
?vdebug("find the corresponding mib view (for ~p)",[ViewName]),
100
get_mib_view(ViewName).
102
check_auth({'EXIT', Error}) -> exit(Error);
103
check_auth({discarded, Reason}) -> {discarded, Reason};
104
check_auth(Res) -> {ok, Res}.
106
%%-----------------------------------------------------------------
107
%% Returns a list of {ViewSubtree, ViewMask, ViewType}
108
%% The view table is index by ViewIndex, ViewSubtree,
109
%% so a next on ViewIndex returns the first
110
%% key in the table >= ViewIndex.
111
%%-----------------------------------------------------------------
112
get_mib_view(ViewName) ->
113
ViewKey = [length(ViewName) | ViewName],
114
case snmp_view_based_acm_mib:table_next(vacmViewTreeFamilyTable,
117
{discarded, noSuchView};
119
case split_prefix(ViewKey, Indexes) of
121
loop_mib_view(ViewKey, Subtree, Indexes, []);
123
{discarded, noSuchView}
127
split_prefix([H|T], [H|T2]) -> split_prefix(T,T2);
128
split_prefix([], Rest) -> {ok, Rest};
129
split_prefix(_, _) -> false.
132
%% ViewName is including length from now on
133
loop_mib_view(ViewName, Subtree, Indexes, MibView) ->
134
[{value, Mask}, {value, Type}, {value, Status}] =
135
snmp_view_based_acm_mib:vacmViewTreeFamilyTable(
137
[?vacmViewTreeFamilyMask,
138
?vacmViewTreeFamilyType,
139
?vacmViewTreeFamilyStatus]),
142
?'RowStatus_active' ->
143
[_Length | Tree] = Subtree,
144
[{Tree, Mask, Type} | MibView];
148
case snmp_view_based_acm_mib:table_next(vacmViewTreeFamilyTable,
150
endOfTable -> NextMibView;
152
case split_prefix(ViewName, NextIndexes) of
154
loop_mib_view(ViewName, NextSubTree, NextIndexes,
161
%%%-----------------------------------------------------------------
162
%%% 1b. The ordered ets table that implements vacmAccessTable
163
%%%-----------------------------------------------------------------
166
init(Dir, terminate).
168
init(Dir, InitError) ->
169
FName = filename:join(Dir, "snmpa_vacm.db"),
170
case file:read_file_info(FName) of
172
%% File exists - we must check this, since ets doesn't tell
173
%% us the reason in case of error...
174
case ets:file2tab(FName) of
178
user_err("Corrupt VACM database ~p", [FName]),
181
throw({error, {file2tab, FName, Reason}});
183
%% Rename old file (for later analyzes)
184
Saved = FName ++ ".saved",
185
file:rename(FName, Saved),
187
[public, ordered_set, named_table])
191
ets:new(snmpa_vacm, [public, ordered_set, named_table])
193
ets:insert(snmp_agent_table, {snmpa_vacm_file, FName}),
198
BackupFile = filename:join(BackupDir, "snmpa_vacm.db"),
199
ets:tab2file(snmpa_vacm, BackupFile).
202
%% Ret: {ok, ViewName} | {error, Reason}
203
get_view_name(ViewType, GroupName, ContextName, SecModel, SecLevel) ->
204
GroupKey = [length(GroupName) | GroupName],
205
case get_access_row(GroupKey, ContextName, SecModel, SecLevel) of
207
{discarded, noAccessEntry};
209
?vtrace("get_view_name -> Row: ~n ~p", [Row]),
212
read -> element(?vacmAReadViewName, Row);
213
write -> element(?vacmAWriteViewName, Row);
214
notify -> element(?vacmANotifyViewName, Row)
218
?vtrace("get_view_name -> not found when"
223
"~n SecLevel: ~p", [ViewType, GroupName,
224
ContextName, SecModel,
226
{discarded, noSuchView};
233
case ets:lookup(snmpa_vacm, Key) of
234
[{_Key, Row}] -> {ok, Row};
239
case ets:next(snmpa_vacm, Key) of
240
'$end_of_table' -> false;
242
case ets:lookup(snmpa_vacm, NextKey) of
248
insert(Entries) -> insert(Entries, true).
250
insert(Entries, Dump) ->
251
lists:foreach(fun(Entry) -> ets:insert(snmpa_vacm, Entry) end, Entries),
255
ets:delete(snmpa_vacm, Key),
264
[{_, FName}] = ets:lookup(snmp_agent_table, snmpa_vacm_file),
265
TmpName = FName ++ ".tmp",
266
case ets:tab2file(snmpa_vacm, TmpName) of
268
case file:rename(TmpName, FName) of
271
Else -> % What is this? Undocumented return code...
272
user_err("Warning: could not move VACM db ~p"
273
" (~p)", [FName, Else])
276
user_err("Warning: could not save vacm db ~p (~p)",
281
%%-----------------------------------------------------------------
283
%% Procedure is defined in the descr. of vacmAccessTable.
285
%% for (each entry with matching group name, context, secmodel and seclevel)
287
%% rate the entry; if it's score is > prev max score, keep it
290
%% Rating: The procedure says to keep entries in order
291
%% 1. matching secmodel ('any'(0) or same(1) is ok)
292
%% 2. matching contextprefix (exact(1) or prefix(0) is ok)
293
%% 3. longest prefix (0..32)
294
%% 4. highest secLevel (noAuthNoPriv(0) < authNoPriv(1) < authPriv(2))
295
%% We give each entry a single rating number according to this order.
296
%% The number is chosen so that a higher number gives a better
297
%% entry, according to the order above.
299
%% secLevel + (3 * prefix_len) + (99 * match_prefix) + (198 * match_secmodel)
301
%% Optimisation: Maybe the most common case is that there
302
%% is just one matching entry, and it matches exact. We could do
303
%% an exact lookup for this entry; if we find one, use it, otherwise
305
%%-----------------------------------------------------------------
306
get_access_row(GroupKey, ContextName, SecModel, SecLevel) ->
307
%% First, try the optimisation...
309
GroupKey ++ [length(ContextName) | ContextName] ++ [SecModel,SecLevel],
310
case ets:lookup(snmpa_vacm, ExactKey) of
313
_ -> % Otherwise, perform the alg
314
get_access_row(GroupKey, GroupKey, ContextName,
315
SecModel, SecLevel, 0, undefined)
318
get_access_row(Key, GroupKey, ContextName, SecModel, SecLevel, Score, Found) ->
319
case get_next_row(Key) of
321
when element(?vacmAStatus, Row) == ?'RowStatus_active'->
322
case catch score(NextKey, GroupKey, ContextName,
323
element(?vacmAContextMatch, Row),
324
SecModel, SecLevel) of
325
{ok, NScore} when NScore > Score ->
326
get_access_row(NextKey, GroupKey, ContextName,
327
SecModel, SecLevel, NScore, Row);
328
{ok, _} -> % e.g. a throwed {ok, 0}
329
get_access_row(NextKey, GroupKey, ContextName,
330
SecModel, SecLevel, Score, Found);
334
{NextKey, _InvalidRow} ->
335
get_access_row(NextKey, GroupKey, ContextName, SecModel,
336
SecLevel, Score, Found);
343
score(Key, GroupKey, ContextName, Match, SecModel, SecLevel) ->
344
[CtxLen | Rest1] = chop_off_group(GroupKey, Key),
345
{NPrefix, [VSecModel, VSecLevel]} =
346
chop_off_context(ContextName, Rest1, 0, CtxLen, Match),
347
%% Make sure the vacmSecModel is valid (any or matching)
348
NSecModel = case VSecModel of
353
%% Make sure the vacmSecLevel is less than the requested
355
VSecLevel =< SecLevel -> VSecLevel - 1;
356
true -> throw({ok, 0})
358
{ok, NSecLevel + 3*CtxLen + NPrefix + NSecModel}.
362
chop_off_group([H|T], [H|T2]) -> chop_off_group(T, T2);
363
chop_off_group([], Rest) -> Rest;
364
chop_off_group(_, _) -> throw(false).
366
chop_off_context([H|T], [H|T2], Cnt, Len, Match) when Cnt < Len ->
367
chop_off_context(T, T2, Cnt+1, Len, Match);
368
chop_off_context([], Rest, _Len, _Len, _Match) ->
369
%% We have exact match; don't care about Match
371
chop_off_context(_, Rest, Len, Len, ?vacmAccessContextMatch_prefix) ->
372
%% We have a prefix match
374
chop_off_context(_Ctx, _Rest, _Cnt, _Len, _Match) ->
375
%% Otherwise, it didn't match!
380
case get_next_row(Oid) of
382
case element(?vacmAStorageType, Row) of
383
?'StorageType_volatile' ->
384
ets:delete(snmpa_vacm, NextOid),
395
snmpa_error:user_err(F, A).
397
% config_err(F, A) ->
398
% snmpa_error:config_err(F, A).