snmpa_vacm.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 399 行
ERL
399 行
%% ``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 via the world wide web 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.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %% $Id$%%-module(snmpa_vacm).-export([get_mib_view/5]).-export([init/1, init/2, backup/1]).-export([delete/1, get_row/1, get_next_row/1, insert/1, insert/2, dump_table/0]).-include("SNMPv2-TC.hrl").-include("SNMP-VIEW-BASED-ACM-MIB.hrl").-include("SNMP-FRAMEWORK-MIB.hrl").-include("snmp_types.hrl").-include("snmpa_vacm.hrl").-define(VMODULE,"VACM").-include("snmp_verbosity.hrl").%%%-----------------------------------------------------------------%%% Access Control Module for VACM (see also snmpa_acm)%%% This module implements:%%% 1. access control functions for VACM%%% 2. vacmAccessTable as an ordered ets table%%%%%% This version of VACM handles v1, v2c and v3.%%%-----------------------------------------------------------------%%%-----------------------------------------------------------------%%% 1. access control functions for VACM%%%-----------------------------------------------------------------%%-----------------------------------------------------------------%% Func: get_mib_view/5 -> {ok, ViewName} | %% {discarded, Reason}%% Types: ViewType = read | write | notify%% SecModel = ?SEC_* (see snmp_types.hrl)%% SecName = string()%% SecLevel = ?'SnmpSecurityLevel_*' (see SNMP-FRAMEWORK-MIB.hrl)%% ContextName = string()%% Purpose: This function is used to map VACM parameters to a mib%% view.%%-----------------------------------------------------------------get_mib_view(ViewType, SecModel, SecName, SecLevel, ContextName) -> check_auth(catch auth(ViewType, SecModel, SecName, SecLevel, ContextName)).%% Follows the procedure in rfc2275auth(ViewType, SecModel, SecName, SecLevel, ContextName) -> % 3.2.1 - Check that the context is known to us ?vdebug("check that the context (~p) is known to us",[ContextName]), case snmp_view_based_acm_mib:vacmContextTable(get, ContextName, [?vacmContextName]) of [_Found] -> ok; _ -> snmpa_mpd:inc(snmpUnknownContexts), throw({discarded, noSuchContext}) end, % 3.2.2 - Check that the SecModel and SecName is valid ?vdebug("check that SecModel (~p) and SecName (~p) is valid", [SecModel,SecName]), GroupName = case snmp_view_based_acm_mib:get(vacmSecurityToGroupTable, [SecModel, length(SecName) | SecName], [?vacmGroupName, ?vacmSecurityToGroupStatus]) of [{value, GN}, {value, ?'RowStatus_active'}] -> GN; [{value, _GN}, {value, RowStatus}] -> ?vlog("valid SecModel and SecName but wrong row status:" "~n RowStatus: ~p", [RowStatus]), throw({discarded, noGroupName}); _ -> throw({discarded, noGroupName}) end, % 3.2.3-4 - Find an access entry and its view name ?vdebug("find an access entry and its view name",[]), ViewName = case get_view_name(ViewType, GroupName, ContextName, SecModel, SecLevel) of {ok, VN} -> VN; Error -> throw(Error) end, % 3.2.5a - Find the corresponding mib view ?vdebug("find the corresponding mib view (for ~p)",[ViewName]), get_mib_view(ViewName).check_auth({'EXIT', Error}) -> exit(Error);check_auth({discarded, Reason}) -> {discarded, Reason};check_auth(Res) -> {ok, Res}.%%-----------------------------------------------------------------%% Returns a list of {ViewSubtree, ViewMask, ViewType}%% The view table is index by ViewIndex, ViewSubtree,%% so a next on ViewIndex returns the first%% key in the table >= ViewIndex.%%-----------------------------------------------------------------get_mib_view(ViewName) -> ViewKey = [length(ViewName) | ViewName], case snmp_view_based_acm_mib:table_next(vacmViewTreeFamilyTable, ViewKey) of endOfTable -> {discarded, noSuchView}; Indexes -> case split_prefix(ViewKey, Indexes) of {ok, Subtree} -> loop_mib_view(ViewKey, Subtree, Indexes, []); false -> {discarded, noSuchView} end end.split_prefix([H|T], [H|T2]) -> split_prefix(T,T2);split_prefix([], Rest) -> {ok, Rest};split_prefix(_, _) -> false. %% ViewName is including length from now onloop_mib_view(ViewName, Subtree, Indexes, MibView) -> [{value, Mask}, {value, Type}, {value, Status}] = snmp_view_based_acm_mib:vacmViewTreeFamilyTable( get, Indexes, [?vacmViewTreeFamilyMask, ?vacmViewTreeFamilyType, ?vacmViewTreeFamilyStatus]), NextMibView = case Status of ?'RowStatus_active' -> [_Length | Tree] = Subtree, [{Tree, Mask, Type} | MibView]; _ -> MibView end, case snmp_view_based_acm_mib:table_next(vacmViewTreeFamilyTable, Indexes) of endOfTable -> NextMibView; NextIndexes -> case split_prefix(ViewName, NextIndexes) of {ok, NextSubTree} -> loop_mib_view(ViewName, NextSubTree, NextIndexes, NextMibView); false -> NextMibView end end.%%%-----------------------------------------------------------------%%% 1b. The ordered ets table that implements vacmAccessTable%%%-----------------------------------------------------------------init(Dir) -> init(Dir, terminate).init(Dir, InitError) -> FName = filename:join(Dir, "snmpa_vacm.db"), case file:read_file_info(FName) of {ok, _} -> %% File exists - we must check this, since ets doesn't tell %% us the reason in case of error... case ets:file2tab(FName) of {ok, _Tab} -> gc_tab([]); {error, Reason} -> user_err("Corrupt VACM database ~p", [FName]), case InitError of terminate -> throw({error, {file2tab, FName, Reason}}); _ -> %% Rename old file (for later analyzes) Saved = FName ++ ".saved", file:rename(FName, Saved), ets:new(snmpa_vacm, [public, ordered_set, named_table]) end end; {error, _} -> ets:new(snmpa_vacm, [public, ordered_set, named_table]) end, ets:insert(snmp_agent_table, {snmpa_vacm_file, FName}), {ok, FName}.backup(BackupDir) -> BackupFile = filename:join(BackupDir, "snmpa_vacm.db"), ets:tab2file(snmpa_vacm, BackupFile).%% Ret: {ok, ViewName} | {error, Reason}get_view_name(ViewType, GroupName, ContextName, SecModel, SecLevel) -> GroupKey = [length(GroupName) | GroupName], case get_access_row(GroupKey, ContextName, SecModel, SecLevel) of undefined -> {discarded, noAccessEntry}; Row -> ?vtrace("get_view_name -> Row: ~n ~p", [Row]), ViewName = case ViewType of read -> element(?vacmAReadViewName, Row); write -> element(?vacmAWriteViewName, Row); notify -> element(?vacmANotifyViewName, Row) end, case ViewName of "" -> ?vtrace("get_view_name -> not found when" "~n ViewType: ~p" "~n GroupName: ~p" "~n ContextName: ~p" "~n SecModel: ~p" "~n SecLevel: ~p", [ViewType, GroupName, ContextName, SecModel, SecLevel]), {discarded, noSuchView}; _ -> {ok, ViewName} end end.get_row(Key) -> case ets:lookup(snmpa_vacm, Key) of [{_Key, Row}] -> {ok, Row}; _ -> false end.get_next_row(Key) -> case ets:next(snmpa_vacm, Key) of '$end_of_table' -> false; NextKey -> case ets:lookup(snmpa_vacm, NextKey) of [Entry] -> Entry; _ -> false end end.insert(Entries) -> insert(Entries, true).insert(Entries, Dump) -> lists:foreach(fun(Entry) -> ets:insert(snmpa_vacm, Entry) end, Entries), dump_table(Dump).delete(Key) -> ets:delete(snmpa_vacm, Key), dump_table().dump_table(true) -> dump_table();dump_table(_) -> ok.dump_table() -> [{_, FName}] = ets:lookup(snmp_agent_table, snmpa_vacm_file), TmpName = FName ++ ".tmp", case ets:tab2file(snmpa_vacm, TmpName) of ok -> case file:rename(TmpName, FName) of ok -> ok; Else -> % What is this? Undocumented return code... user_err("Warning: could not move VACM db ~p" " (~p)", [FName, Else]) end; {error, Reason} -> user_err("Warning: could not save vacm db ~p (~p)", [FName, Reason]) end.%%-----------------------------------------------------------------%% Alg.%% Procedure is defined in the descr. of vacmAccessTable.%%%% for (each entry with matching group name, context, secmodel and seclevel)%% {%% rate the entry; if it's score is > prev max score, keep it%% }%%%% Rating: The procedure says to keep entries in order%% 1. matching secmodel ('any'(0) or same(1) is ok)%% 2. matching contextprefix (exact(1) or prefix(0) is ok)%% 3. longest prefix (0..32)%% 4. highest secLevel (noAuthNoPriv(0) < authNoPriv(1) < authPriv(2))%% We give each entry a single rating number according to this order.%% The number is chosen so that a higher number gives a better%% entry, according to the order above.%% The number is:%% secLevel + (3 * prefix_len) + (99 * match_prefix) + (198 * match_secmodel)%%%% Optimisation: Maybe the most common case is that there%% is just one matching entry, and it matches exact. We could do%% an exact lookup for this entry; if we find one, use it, otherwise%% perform this alg.%%-----------------------------------------------------------------get_access_row(GroupKey, ContextName, SecModel, SecLevel) -> %% First, try the optimisation... ExactKey = GroupKey ++ [length(ContextName) | ContextName] ++ [SecModel,SecLevel], case ets:lookup(snmpa_vacm, ExactKey) of [{_Key, Row}] -> Row; _ -> % Otherwise, perform the alg get_access_row(GroupKey, GroupKey, ContextName, SecModel, SecLevel, 0, undefined) end.get_access_row(Key, GroupKey, ContextName, SecModel, SecLevel, Score, Found) -> case get_next_row(Key) of {NextKey, Row} when element(?vacmAStatus, Row) == ?'RowStatus_active'-> case catch score(NextKey, GroupKey, ContextName, element(?vacmAContextMatch, Row), SecModel, SecLevel) of {ok, NScore} when NScore > Score -> get_access_row(NextKey, GroupKey, ContextName, SecModel, SecLevel, NScore, Row); {ok, _} -> % e.g. a throwed {ok, 0} get_access_row(NextKey, GroupKey, ContextName, SecModel, SecLevel, Score, Found); false -> Found end; {NextKey, _InvalidRow} -> get_access_row(NextKey, GroupKey, ContextName, SecModel, SecLevel, Score, Found); false -> Found end. score(Key, GroupKey, ContextName, Match, SecModel, SecLevel) -> [CtxLen | Rest1] = chop_off_group(GroupKey, Key), {NPrefix, [VSecModel, VSecLevel]} = chop_off_context(ContextName, Rest1, 0, CtxLen, Match), %% Make sure the vacmSecModel is valid (any or matching) NSecModel = case VSecModel of SecModel -> 198; ?SEC_ANY -> 0; _ -> throw({ok, 0}) end, %% Make sure the vacmSecLevel is less than the requested NSecLevel = if VSecLevel =< SecLevel -> VSecLevel - 1; true -> throw({ok, 0}) end, {ok, NSecLevel + 3*CtxLen + NPrefix + NSecModel}. chop_off_group([H|T], [H|T2]) -> chop_off_group(T, T2);chop_off_group([], Rest) -> Rest;chop_off_group(_, _) -> throw(false).chop_off_context([H|T], [H|T2], Cnt, Len, Match) when Cnt < Len -> chop_off_context(T, T2, Cnt+1, Len, Match);chop_off_context([], Rest, _Len, _Len, _Match) -> %% We have exact match; don't care about Match {99, Rest};chop_off_context(_, Rest, Len, Len, ?vacmAccessContextMatch_prefix) -> %% We have a prefix match {0, Rest};chop_off_context(_Ctx, _Rest, _Cnt, _Len, _Match) -> %% Otherwise, it didn't match! throw({ok, 0}).gc_tab(Oid) -> case get_next_row(Oid) of {NextOid, Row} -> case element(?vacmAStorageType, Row) of ?'StorageType_volatile' -> ets:delete(snmpa_vacm, NextOid), gc_tab(NextOid); _ -> gc_tab(NextOid) end; false -> ok end.user_err(F, A) -> snmpa_error:user_err(F, A).% config_err(F, A) ->% snmpa_error:config_err(F, A).
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?