snmpa_acm.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 315 行
ERL
315 行
%% ``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_acm).-behaviour(snmpa_authentication_service).-export([init_check_access/2, get_root_mib_view/0, error2status/1, validate_mib_view/2, validate_all_mib_view/2, is_definitely_not_in_mib_view/2]).-include("snmp_types.hrl").-include("STANDARD-MIB.hrl").-include("SNMP-FRAMEWORK-MIB.hrl").-include("SNMPv2-TM.hrl").-define(VMODULE,"ACM").-include("snmp_verbosity.hrl").%%%-----------------------------------------------------------------%%% This module implements the Access Control Model part of the%%% multi-lingual SNMP agent. It contains generic function not%%% tied to a specific model, but in this version it uses VACM.%%%%%% Note that we don't follow the isAccessAllowed Abstract Service%%% Interface defined in rfc2271. We implement an optimization%%% of that ASI. Since the mib view is the same for all variable%%% bindings in a PDU, there is no need to recalculate the mib%%% view for each variable. Therefore, one function%%% (init_check_access/2) is used to find the mib view, and then%%% each variable is checked against this mib view.%%%%%% Access checking is done in several steps. First, the version-%%% specific MPD (see snmpa_mpd) creates data used by VACM. This%%% means that the format of this data is known by both the MPD and%%% the ACM. When the master agent wants to check the access to a%%% Pdu, it first calls init_check_access/2, which returns a MibView%%% that can be used to check access of individual variables.%%%-----------------------------------------------------------------%%-----------------------------------------------------------------%% Func: init_check_access(Pdu, ACMData) ->%% {ok, MibView, ContextName} |%% {error, Reason} |%% {discarded, Variable, Reason}%% Types: Pdu = #pdu%% ACMData = acm_data() = {community, Community, Address} |%% {v3, MsgID, SecModel, SecName, SecLevel,%% ContextEngineID, ContextName, SecData}%% Community = string()%% Address = ip() ++ udp() (list)%% MsgID = integer() <not used>%% SecModel = ?SEC_* (see snmp_types.hrl)%% SecName = string()%% SecLevel = ?'SnmpSecurityLevel_*' (see SNMP-FRAMEWORK-MIB.hrl)%% ContextEngineID = string() <not used>%% ContextName = string()%% SecData = <not used>%% Variable = snmpInBadCommunityNames |%% snmpInBadCommunityUses |%% snmpInASNParseErrs%% Reason = snmp_message_decoding |%% {bad_community_name, Address, Community}} |%% {invalid_access, Access, Op}%% %% Purpose: Called once for each Pdu. Returns a MibView%% which is later used for each variable in the pdu.%% The authenticationFailure trap is sent (maybe) when the auth.%% procedure evaluates to unauthentic,%%%% NOTE: This function is executed in the Master agents's context%%-----------------------------------------------------------------init_check_access(Pdu, ACMData) -> case init_ca(Pdu, ACMData) of {ok, MibView, ContextName} -> {ok, MibView, ContextName}; {discarded, Reason} -> {error, Reason}; {authentication_failure, Variable, Reason} -> handle_authentication_failure(), {discarded, Variable, Reason} end.error2status(noSuchView) -> authorizationError;error2status(noAccessEntry) -> authorizationError;error2status(noGroupName) -> authorizationError;error2status(_) -> genErr. %%-----------------------------------------------------------------%% Func: init_ca(Pdu, ACMData) ->%% {ok, MibView} |%% {discarded, Reason} |%% {authentication_failure, Variable, Reason}%%%% error: an error response will be sent%% discarded: no error response is sent%% authentication_failure: no error response is sent, a trap is generated%%-----------------------------------------------------------------init_ca(Pdu, {community, SecModel, Community, TAddr}) -> %% This is a v1 or v2c request. Use SNMP-COMMUNITY-MIB to %% map the community to vacm parameters. ?vtrace("check access for ~n" " Pdu: ~p~n" " Security model: ~p~n" " Community: ~s",[Pdu,SecModel,Community]), ViewType = case Pdu#pdu.type of 'set-request' -> write; _ -> read end, ?vtrace("View type: ~p",[ViewType]), case snmp_community_mib:community2vacm(Community, {?snmpUDPDomain,TAddr}) of {SecName, _ContextEngineId, ContextName} -> %% Maybe we should check that the contextEngineID matches the %% local engineID? It better, since we don't impl. proxy. ?vtrace("get mib view" "~n Security name: ~p" "~n Context name: ~p",[SecName,ContextName]), case snmpa_vacm:get_mib_view(ViewType, SecModel, SecName, ?'SnmpSecurityLevel_noAuthNoPriv', ContextName) of {ok, MibView} -> put(sec_model, SecModel), put(sec_name, SecName), {ok, MibView, ContextName}; {discarded, Reason} -> snmpa_mpd:inc(snmpInBadCommunityUses), {discarded, Reason} end; undefined -> {authentication_failure, snmpInBadCommunityNames, {bad_community_name, TAddr, Community}} end;init_ca(Pdu, {v3, _MsgID, SecModel, SecName, SecLevel, _ContextEngineID, ContextName, _SecData}) -> ?vtrace("check v3 access for ~n" " Pdu: ~p~n" " Security model: ~p~n" " Security name: ~p~n" " Security level: ~p",[Pdu,SecModel,SecName,SecLevel]), ViewType = case Pdu#pdu.type of 'set-request' -> write; _ -> read end, ?vtrace("View type: ~p",[ViewType]), %% Convert the msgflag value to a ?'SnmpSecurityLevel*' SL = case SecLevel of 0 -> ?'SnmpSecurityLevel_noAuthNoPriv'; 1 -> ?'SnmpSecurityLevel_authNoPriv'; 3 -> ?'SnmpSecurityLevel_authPriv' end, put(sec_model, SecModel), put(sec_name, SecName), case snmpa_vacm:get_mib_view(ViewType, SecModel, SecName, SL, ContextName) of {ok, MibView} -> {ok, MibView, ContextName}; Else -> Else end.%%-----------------------------------------------------------------%% Func: check(Res) -> {ok, MibView} | {discarded, Variable, Reason}%% Args: Res = {ok, AccessFunc} | {authentication_failure, Variable, Reason%%-----------------------------------------------------------------%%-----------------------------------------------------------------%% NOTE: This function is executed in the Master agents's context%% Do a GET to retrieve the value for snmpEnableAuthenTraps. A%% user may have another impl. than default for this variable.%%-----------------------------------------------------------------handle_authentication_failure() -> case snmpa_agent:do_get(get_root_mib_view(), [#varbind{oid = ?snmpEnableAuthenTraps_instance}], true) of {noError, _, [#varbind{value = ?snmpEnableAuthenTraps_enabled}]} -> snmpa:send_notification(self(), authenticationFailure, no_receiver); _ -> ok end.%%%-----------------------------------------------------------------%%% MIB View handling%%%-----------------------------------------------------------------get_root_mib_view() -> [{[1], [], ?view_included}].%%-----------------------------------------------------------------%% Returns true if Oid is in the MibView, false%% otherwise.%% Alg: (defined in SNMP-VIEW-BASED-ACM-MIB)%% For each family (= {SubTree, Mask, Type}), check if Oid%% belongs to that family. For each family that Oid belong to,%% get the longest. If two or more are longest, get the%% lexicografically greatest. Check the type of this family. If%% included, then Oid belongs to the MibView, otherwise it%% does not.%% Optimisation: Do only one loop, and kepp the largest sofar.%% When we find a family that Oid belongs to, check if it is%% larger than the largest.%%-----------------------------------------------------------------validate_mib_view(Oid, MibView) -> case get_largest_family(MibView, Oid, undefined) of {_, _, ?view_included} -> true; _ -> false end.get_largest_family([{SubTree, Mask, Type} | T], Oid, Res) -> case check_mask(Oid, SubTree, Mask) of true -> get_largest_family(T, Oid, add_res(length(SubTree), SubTree, Type, Res)); false -> get_largest_family(T, Oid, Res) end;get_largest_family([], _Oid, Res) -> Res.%%-----------------------------------------------------------------%% We keep only the largest (first longest SubTree, and then %% lexicografically greatest) SubTree.%%-----------------------------------------------------------------add_res(Len, SubTree, Type, undefined) -> {Len, SubTree, Type};add_res(Len, SubTree, Type, {MaxLen, _MaxS, _MaxT}) when Len > MaxLen -> {Len, SubTree, Type};add_res(Len, SubTree, Type, {MaxLen, MaxS, MaxT}) when Len == MaxLen -> if SubTree > MaxS -> {Len, SubTree, Type}; true -> {MaxLen, MaxS, MaxT} end;add_res(_Len, _SubTree, _Type, MaxRes) -> MaxRes.%% 1 in mask is exact match, 0 is wildcard.%% If mask is shorter than SubTree, its regarded%% as being all ones.check_mask(_Oid, [], _Mask) -> true;check_mask([X | Xs], [X | Ys], [1 | Ms]) -> check_mask(Xs, Ys, Ms);check_mask([X | Xs], [X | Ys], []) -> check_mask(Xs, Ys, []);check_mask([_X | Xs], [_Y | Ys], [0 | Ms]) -> check_mask(Xs, Ys, Ms);check_mask(_, _, _) -> false.%%-----------------------------------------------------------------%% Validates all oids in the Varbinds list towards the MibView.%%-----------------------------------------------------------------validate_all_mib_view([#varbind{oid = Oid, org_index = Index} | Varbinds], MibView) -> ?vtrace("validate_all_mib_view -> entry with" "~n Oid: ~p" "~n Index: ~p", [Oid, Index]), case validate_mib_view(Oid, MibView) of true -> validate_all_mib_view(Varbinds, MibView); false -> {false, Index} end;validate_all_mib_view([], _MibView) -> ?vtrace("validate_all_mib_view -> done", []), true.%%-----------------------------------------------------------------%% This function is used to optimize the next operation in%% snmpa_mib_data. If we get to a node in the tree where we can%% determine that we are guaranteed to be outside the mibview,%% we don't have to continue the search in the that tree (Actually%% we will, because we only check this at leafs. But we won't%% go into tables or subagents, and that's the important%% optimization.) For now, this function isn't that sophisticated;%% it just checks that there is really no family in the mibview%% that the Oid (or any other oids with Oid as prefix) may be%% included in. Perhaps this function easily could be more%% intelligent.%%-----------------------------------------------------------------is_definitely_not_in_mib_view(Oid, [{SubTree, Mask,?view_included}|T]) -> case check_maybe_mask(Oid, SubTree, Mask) of true -> false; false -> is_definitely_not_in_mib_view(Oid, T) end;is_definitely_not_in_mib_view(Oid, [{_SubTree, _Mask,?view_excluded}|T]) -> is_definitely_not_in_mib_view(Oid, T);is_definitely_not_in_mib_view(_Oid, []) -> true. %%-----------------------------------------------------------------%% As check_mask, BUT if Oid < SubTree and sofar good, we%% return true. As Oid get larger we may decide.%%-----------------------------------------------------------------check_maybe_mask(_Oid, [], _Mask) -> true;check_maybe_mask([X | Xs], [X | Ys], [1 | Ms]) -> check_maybe_mask(Xs, Ys, Ms);check_maybe_mask([X | Xs], [X | Ys], []) -> check_maybe_mask(Xs, Ys, []);check_maybe_mask([_X | Xs], [_Y | Ys], [0 | Ms]) -> check_maybe_mask(Xs, Ys, Ms);check_maybe_mask([_X | _Xs], [_Y | _Ys], _) -> false;check_maybe_mask(_, _, _) -> true.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?