%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1999-2011. 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% %% -module(snmpa_usm). %% Avoid warning for local function error/1 clashing with autoimported BIF. -compile({no_auto_import,[error/1]}). %% Avoid warning for local function error/2 clashing with autoimported BIF. -compile({no_auto_import,[error/2]}). -export([ process_incoming_msg/4, process_incoming_msg/5, generate_outgoing_msg/5, generate_outgoing_msg/6, generate_discovery_msg/4, generate_discovery_msg/5, current_statsNotInTimeWindows_vb/0 ]). -define(SNMP_USE_V3, true). -include("snmp_types.hrl"). -include("SNMP-USER-BASED-SM-MIB.hrl"). -include("SNMP-USM-AES-MIB.hrl"). -include("SNMPv2-TC.hrl"). -define(VMODULE,"A-USM"). -include("snmp_verbosity.hrl"). -include("snmpa_internal.hrl"). %%----------------------------------------------------------------- %% This module implements the User Based Security Model for SNMP, %% as defined in rfc2274. %%----------------------------------------------------------------- %% Columns not accessible via SNMP -define(usmUserAuthKey, 14). -define(usmUserPrivKey, 15). -define(i32(Int), (Int bsr 24) band 255, (Int bsr 16) band 255, (Int bsr 8) band 255, Int band 255). -define(i64(Int), (Int bsr 56) band 255, (Int bsr 48) band 255, (Int bsr 40) band 255, (Int bsr 32) band 255, (Int bsr 24) band 255, (Int bsr 16) band 255, (Int bsr 8) band 255, Int band 255). %%----------------------------------------------------------------- %% Func: process_incoming_msg(Packet, Data, SecParams, SecLevel) -> %% {ok, {SecEngineID, SecName, ScopedPDUBytes, SecData}} | %% {error, Reason} | {error, Reason, ErrorInfo} %% Return value may be throwed. %% Types: Reason -> term() %% Purpose: %%----------------------------------------------------------------- process_incoming_msg(Packet, Data, SecParams, SecLevel) -> LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, process_incoming_msg(Packet, Data, SecParams, SecLevel, LocalEngineID). process_incoming_msg(Packet, Data, SecParams, SecLevel, LocalEngineID) -> TermDiscoEnabled = is_terminating_discovery_enabled(), TermTriggerUsername = terminating_trigger_username(), %% 3.2.1 ?vtrace("process_incoming_msg -> check security parms: 3.2.1",[]), UsmSecParams = case catch snmp_pdus:dec_usm_security_parameters(SecParams) of {'EXIT', Reason} -> inc(snmpInASNParseErrs), error({parseError, Reason}, []); Res -> Res end, case UsmSecParams of #usmSecurityParameters{msgAuthoritativeEngineID = MsgAuthEngineID, msgUserName = TermTriggerUsername} when TermDiscoEnabled =:= true -> %% Step 1 discovery message ?vtrace("process_incoming_msg -> [~p] discovery step 1", [TermTriggerUsername]), process_discovery_msg(MsgAuthEngineID, Data, SecLevel); #usmSecurityParameters{msgAuthoritativeEngineID = MsgAuthEngineID, msgUserName = MsgUserName} -> ?vlog("process_incoming_msg -> USM security parms: " "~n msgAuthEngineID: ~w" "~n userName: ~p", [MsgAuthEngineID, MsgUserName]), %% 3.2.3 ?vtrace("process_incoming_msg -> check engine id: 3.2.3",[]), case snmp_user_based_sm_mib:is_engine_id_known(MsgAuthEngineID) of true -> ok; false -> SecData1 = [MsgUserName], error(usmStatsUnknownEngineIDs, ?usmStatsUnknownEngineIDs_instance, %% OTP-3542 undefined, [{sec_data, SecData1}]) end, %% 3.2.4 ?vtrace("process_incoming_msg -> retrieve usm user: 3.2.4",[]), UsmUser = case snmp_user_based_sm_mib:get_user(MsgAuthEngineID, MsgUserName) of User when element(?usmUserStatus, User) =:= ?'RowStatus_active' -> User; {_, Name,_,_,_,_,_,_,_,_,_,_,_, RowStatus,_,_} -> ?vdebug("process_incoming_msg -> " "found user ~p with wrong row status: ~p", [Name, RowStatus]), SecData2 = [MsgUserName], error(usmStatsUnknownUserNames, ?usmStatsUnknownUserNames_instance, %% OTP-3542 undefined, [{sec_data, SecData2}]); _ -> % undefined or not active user SecData2 = [MsgUserName], error(usmStatsUnknownUserNames, ?usmStatsUnknownUserNames_instance, %% OTP-3542 undefined, [{sec_data, SecData2}]) end, SecName = element(?usmUserSecurityName, UsmUser), ?vtrace("process_incoming_msg -> securityName: ~p",[SecName]), %% 3.2.5 - implicit in following checks %% 3.2.6 - 3.2.7 ?vtrace("process_incoming_msg -> " "authenticate incoming: 3.2.5 - 3.2.7" "~n ~p",[UsmUser]), DiscoOrPlain = authenticate_incoming(Packet, UsmSecParams, UsmUser, SecLevel, LocalEngineID), %% 3.2.8 ?vtrace("process_incoming_msg -> " "decrypt scoped data: 3.2.8",[]), ScopedPDUBytes = decrypt(Data, UsmUser, UsmSecParams, SecLevel), %% 3.2.9 %% Means that if AuthKey/PrivKey are changed; %% the old values will be used. ?vtrace("process_incoming_msg -> " "AuthKey/PrivKey are changed - " "use old values: 3.2.9",[]), CachedSecData = {MsgUserName, element(?usmUserAuthProtocol, UsmUser), element(?usmUserPrivProtocol, UsmUser), element(?usmUserAuthKey, UsmUser), element(?usmUserPrivKey, UsmUser)}, {ok, {MsgAuthEngineID, SecName, ScopedPDUBytes, CachedSecData, DiscoOrPlain}} end. %% Process a step 1 discovery message process_discovery_msg(MsgAuthEngineID, Data, SecLevel) -> ?vtrace("process_discovery_msg -> entry with" "~n Data: ~p" "~n SecLevel: ~p", [Data, SecLevel]), case (not snmp_misc:is_priv(SecLevel)) of true -> % noAuthNoPriv ?vtrace("process_discovery_msg -> noAuthNoPriv", []), ScopedPDUBytes = Data, SecData = {"", usmNoAuthProtocol, "", usmNoPrivProtocol, ""}, NewData = {SecData, ?usmStatsUnknownEngineIDs_instance, get_counter(usmStatsUnknownEngineIDs)}, {ok, {MsgAuthEngineID, "", ScopedPDUBytes, NewData, discovery}}; false -> error(usmStatsUnknownEngineIDs, ?usmStatsUnknownEngineIDs_instance, undefined, [{sec_data, ""}]) end. authenticate_incoming(Packet, UsmSecParams, UsmUser, SecLevel, LocalEngineID) -> %% 3.2.6 ?vtrace("authenticate_incoming -> 3.2.6", []), AuthProtocol = element(?usmUserAuthProtocol, UsmUser), #usmSecurityParameters{msgAuthoritativeEngineID = MsgAuthEngineID, msgAuthoritativeEngineBoots = MsgAuthEngineBoots, msgAuthoritativeEngineTime = MsgAuthEngineTime, msgAuthenticationParameters = MsgAuthParams} = UsmSecParams, ?vtrace("authenticate_incoming -> Sec params: " "~n MsgAuthEngineID: ~w" "~n MsgAuthEngineBoots: ~p" "~n MsgAuthEngineTime: ~p", [MsgAuthEngineID, MsgAuthEngineBoots, MsgAuthEngineTime]), case snmp_misc:is_auth(SecLevel) of true -> SecName = element(?usmUserSecurityName, UsmUser), case is_auth(AuthProtocol, element(?usmUserAuthKey, UsmUser), MsgAuthParams, Packet, SecName, MsgAuthEngineID, MsgAuthEngineBoots, MsgAuthEngineTime, LocalEngineID) of discovery -> discovery; true -> plain; false -> error(usmStatsWrongDigests, ?usmStatsWrongDigests_instance, % OTP-5464 SecName) end; false -> % noAuth plain end. authoritative(SecName, MsgAuthEngineBoots, MsgAuthEngineTime, LocalEngineID) -> ?vtrace("authoritative -> entry with" "~n SecName: ~p" "~n MsgAuthEngineBoots: ~p" "~n MsgAuthEngineTime: ~p", [SecName, MsgAuthEngineBoots, MsgAuthEngineTime]), SnmpEngineBoots = get_local_engine_boots(LocalEngineID), ?vtrace("authoritative -> SnmpEngineBoots: ~p", [SnmpEngineBoots]), SnmpEngineTime = get_local_engine_time(LocalEngineID), ?vtrace("authoritative -> SnmpEngineTime: ~p", [SnmpEngineTime]), InTimeWindow = if SnmpEngineBoots =:= 2147483647 -> false; MsgAuthEngineBoots =/= SnmpEngineBoots -> false; MsgAuthEngineTime + 150 < SnmpEngineTime -> false; MsgAuthEngineTime - 150 > SnmpEngineTime -> false; true -> true end, case InTimeWindow of true -> true; false -> %% OTP-4090 (OTP-3542) ?vinfo("NOT in time window: " "~n SecName: ~p" "~n SnmpEngineBoots: ~p" "~n MsgAuthEngineBoots: ~p" "~n SnmpEngineTime: ~p" "~n MsgAuthEngineTime: ~p", [SecName, SnmpEngineBoots, MsgAuthEngineBoots, SnmpEngineTime, MsgAuthEngineTime]), error(usmStatsNotInTimeWindows, ?usmStatsNotInTimeWindows_instance, SecName, [{securityLevel, 1}]) % authNoPriv end. non_authoritative(SecName, MsgAuthEngineID, MsgAuthEngineBoots, MsgAuthEngineTime) -> ?vtrace("non_authoritative -> entry with" "~n SecName: ~p" "~n MsgAuthEngineID: ~p" "~n MsgAuthEngineBoots: ~p" "~n MsgAuthEngineTime: ~p", [SecName, MsgAuthEngineID, MsgAuthEngineBoots, MsgAuthEngineTime]), SnmpEngineBoots = get_engine_boots(MsgAuthEngineID), SnmpEngineTime = get_engine_time(MsgAuthEngineID), LatestRecvTime = get_engine_latest_time(MsgAuthEngineID), ?vtrace("non_authoritative -> " "~n SnmpEngineBoots: ~p" "~n SnmpEngineTime: ~p" "~n LatestRecvTime: ~p", [SnmpEngineBoots, SnmpEngineTime, LatestRecvTime]), UpdateLCD = if MsgAuthEngineBoots > SnmpEngineBoots -> true; ((MsgAuthEngineBoots =:= SnmpEngineBoots) andalso (MsgAuthEngineTime > LatestRecvTime)) -> true; true -> false end, case UpdateLCD of true -> %% 3.2.7b1 ?vtrace("non_authoritative -> " "update msgAuthoritativeEngineID: 3.2.7b1", []), set_engine_boots(MsgAuthEngineID, MsgAuthEngineBoots), set_engine_time(MsgAuthEngineID, MsgAuthEngineTime), set_engine_latest_time(MsgAuthEngineID, MsgAuthEngineTime); false -> ok end, %% 3.2.7.b2 ?vtrace("non_authoritative -> " "check if message is outside time window: 3.2.7b2", []), InTimeWindow = if SnmpEngineBoots =:= 2147483647 -> false; MsgAuthEngineBoots < SnmpEngineBoots -> false; ((MsgAuthEngineBoots =:= SnmpEngineBoots) andalso (MsgAuthEngineTime < (SnmpEngineTime - 150))) -> false; true -> true end, case InTimeWindow of false -> ?vinfo("NOT in time window: " "~n SecName: ~p" "~n SnmpEngineBoots: ~p" "~n MsgAuthEngineBoots: ~p" "~n SnmpEngineTime: ~p" "~n MsgAuthEngineTime: ~p", [SecName, SnmpEngineBoots, MsgAuthEngineBoots, SnmpEngineTime, MsgAuthEngineTime]), error(notInTimeWindow, []); true -> %% If the previous values where all zero's this is the %% second stage discovery message if ((SnmpEngineBoots =:= 0) andalso (SnmpEngineTime =:= 0) andalso (LatestRecvTime =:= 0)) -> ?vtrace("non_authoritative -> " "[maybe] originating discovery stage 2", []), discovery; true -> true end end. is_auth(?usmNoAuthProtocol, _, _, _, SecName, _, _, _, _) -> % 3.2.5 error(usmStatsUnsupportedSecLevels, ?usmStatsUnsupportedSecLevels_instance, SecName); % OTP-5464 is_auth(AuthProtocol, AuthKey, AuthParams, Packet, SecName, MsgAuthEngineID, MsgAuthEngineBoots, MsgAuthEngineTime, LocalEngineID) -> TermDiscoEnabled = is_terminating_discovery_enabled(), TermDiscoStage2 = terminating_discovery_stage2(), IsAuth = auth_in(AuthProtocol, AuthKey, AuthParams, Packet), ?vtrace("is_auth -> IsAuth: ~p", [IsAuth]), case IsAuth of true -> %% 3.2.7 ?vtrace("is_auth -> " "retrieve EngineBoots and EngineTime: 3.2.7",[]), SnmpEngineID = LocalEngineID, ?vtrace("is_auth -> SnmpEngineID: ~p", [SnmpEngineID]), case MsgAuthEngineID of SnmpEngineID when ((MsgAuthEngineBoots =:= 0) andalso (MsgAuthEngineTime =:= 0) andalso (TermDiscoEnabled =:= true) andalso (TermDiscoStage2 =:= discovery)) -> %% 3.2.7a ?vtrace("is_auth -> terminating discovery stage 2 - discovery",[]), discovery; SnmpEngineID when ((MsgAuthEngineBoots =:= 0) andalso (MsgAuthEngineTime =:= 0) andalso (TermDiscoEnabled =:= true) andalso (TermDiscoStage2 =:= plain)) -> %% 3.2.7a ?vtrace("is_auth -> terminating discovery stage 2 - plain",[]), %% This will *always* result in the manager *not* %% beeing in timewindow authoritative(SecName, MsgAuthEngineBoots, MsgAuthEngineTime, LocalEngineID); SnmpEngineID -> %% 3.2.7a ?vtrace("is_auth -> we are authoritative: 3.2.7a", []), authoritative(SecName, MsgAuthEngineBoots, MsgAuthEngineTime, LocalEngineID); _ -> %% 3.2.7b - we're non-authoritative ?vtrace("is_auth -> we are non-authoritative: 3.2.7b",[]), non_authoritative(SecName, MsgAuthEngineID, MsgAuthEngineBoots, MsgAuthEngineTime) end; false -> false end. decrypt(Data, UsmUser, UsmSecParams, SecLevel) -> case snmp_misc:is_priv(SecLevel) of true -> do_decrypt(Data, UsmUser, UsmSecParams); false -> Data end. do_decrypt(Data, UsmUser, UsmSecParams) -> EncryptedPDU = snmp_pdus:dec_scoped_pdu_data(Data), SecName = element(?usmUserSecurityName, UsmUser), PrivP = element(?usmUserPrivProtocol, UsmUser), PrivKey = element(?usmUserPrivKey, UsmUser), ?vtrace("do_decrypt -> try decrypt with: " "~n SecName: ~p" "~n PrivP: ~p", [SecName, PrivP]), try_decrypt(PrivP, PrivKey, UsmSecParams, EncryptedPDU, SecName). try_decrypt(?usmNoPrivProtocol, _, _, _, SecName) -> % 3.2.5 error(usmStatsUnsupportedSecLevels, ?usmStatsUnsupportedSecLevels_instance, SecName); % OTP-5464 try_decrypt(?usmDESPrivProtocol, PrivKey, UsmSecParams, EncryptedPDU, SecName) -> case (catch des_decrypt(PrivKey, UsmSecParams, EncryptedPDU)) of {ok, DecryptedData} -> DecryptedData; Error -> ?vlog("try_decrypt -> failed DES decrypt" "~n Error: ~p", [Error]), error(usmStatsDecryptionErrors, ?usmStatsDecryptionErrors_instance, % OTP-5464 SecName) end; try_decrypt(?usmAesCfb128Protocol, PrivKey, UsmSecParams, EncryptedPDU, SecName) -> case (catch aes_decrypt(PrivKey, UsmSecParams, EncryptedPDU)) of {ok, DecryptedData} -> DecryptedData; Error -> ?vlog("try_decrypt -> failed AES decrypt" "~n Error: ~p", [Error]), error(usmStatsDecryptionErrors, ?usmStatsDecryptionErrors_instance, % OTP-5464 SecName) end. generate_outgoing_msg(Message, SecEngineID, SecName, SecData, SecLevel) -> LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, generate_outgoing_msg(Message, SecEngineID, SecName, SecData, SecLevel, LocalEngineID). generate_outgoing_msg(Message, SecEngineID, SecName, SecData, SecLevel, LocalEngineID) -> %% 3.1.1 ?vtrace("generate_outgoing_msg -> [3.1.1] entry with" "~n SecEngineID: ~p" "~n SecName: ~p" "~n SecLevel: ~w" "~n LocalEngineID: ~p", [SecEngineID, SecName, SecLevel, LocalEngineID]), {UserName, AuthProtocol, PrivProtocol, AuthKey, PrivKey} = case SecData of [] -> % 3.1.1b %% Not a response - read from LCD case snmp_user_based_sm_mib:get_user_from_security_name( SecEngineID, SecName) of User when element(?usmUserStatus, User) =:= ?'RowStatus_active' -> {element(?usmUserName, User), element(?usmUserAuthProtocol, User), element(?usmUserPrivProtocol, User), element(?usmUserAuthKey, User), element(?usmUserPrivKey, User)}; {_, Name,_,_,_,_,_,_,_,_,_,_,_, RowStatus,_,_} -> ?vdebug("generate_outgoing_msg -> " "found not active user ~p: ~p", [Name, RowStatus]), error(unknownSecurityName); _ -> error(unknownSecurityName) end; [MsgUserName] -> %% This means the user at the engine is unknown {MsgUserName, ?usmNoAuthProtocol, ?usmNoPrivProtocol, "", ""}; _ -> % 3.1.1a SecData end, %% 3.1.4 ?vtrace("generate_outgoing_msg -> [3.1.4]" "~n UserName: ~p" "~n AuthProtocol: ~p" "~n PrivProtocol: ~p", [UserName, AuthProtocol, PrivProtocol]), ScopedPduBytes = Message#message.data, {ScopedPduData, MsgPrivParams} = encrypt(ScopedPduBytes, PrivProtocol, PrivKey, SecLevel), SnmpEngineID = LocalEngineID, ?vtrace("generate_outgoing_msg -> SnmpEngineID: ~p [3.1.6]", [SnmpEngineID]), %% 3.1.6 {MsgAuthEngineBoots, MsgAuthEngineTime} = case snmp_misc:is_auth(SecLevel) of false when SecData =:= [] -> % not a response {0, 0}; false when UserName =:= "" -> % reply (report) to discovery step 1 {0, 0}; true when SecEngineID =/= SnmpEngineID -> {get_engine_boots(SecEngineID), get_engine_time(SecEngineID)}; _ -> {get_local_engine_boots(SnmpEngineID), get_local_engine_time(SnmpEngineID)} end, %% 3.1.5 - 3.1.7 ?vtrace("generate_outgoing_msg -> [3.1.5 - 3.1.7]",[]), UsmSecParams = #usmSecurityParameters{msgAuthoritativeEngineID = SecEngineID, msgAuthoritativeEngineBoots = MsgAuthEngineBoots, msgAuthoritativeEngineTime = MsgAuthEngineTime, msgUserName = UserName, msgPrivacyParameters = MsgPrivParams}, Message2 = Message#message{data = ScopedPduData}, %% 3.1.8 ?vtrace("generate_outgoing_msg -> [3.1.8]",[]), authenticate_outgoing(Message2, UsmSecParams, AuthKey, AuthProtocol, SecLevel). generate_discovery_msg(Message, SecEngineID, SecName, SecLevel) -> generate_discovery_msg(Message, SecEngineID, SecName, SecLevel, ""). generate_discovery_msg(Message, SecEngineID, SecName, SecLevel, InitialUserName) -> ?vtrace("generate_discovery_msg -> entry with" "~n SecEngineID: ~p" "~n SecName: ~p" "~n SecLevel: ~p" "~n InitialUserName: ~p", [SecEngineID, SecName, SecLevel, InitialUserName]), {UserName, AuthProtocol, AuthKey, PrivProtocol, PrivKey} = case SecEngineID of "" -> %% Discovery step 1 %% Nothing except the user name will be used in this %% tuple in this step, but since we need some values, %% we fill in proper ones just in case %% {"initial", usmNoAuthProtocol, "", usmNoPrivProtocol, ""}; %% {"", usmNoAuthProtocol, "", usmNoPrivProtocol, ""}; {InitialUserName, usmNoAuthProtocol, "", usmNoPrivProtocol, ""}; _ -> %% Discovery step 2 case snmp_user_based_sm_mib:get_user_from_security_name( SecEngineID, SecName) of User when element(?usmUserStatus, User) =:= ?'RowStatus_active' -> {element(?usmUserName, User), element(?usmUserAuthProtocol, User), element(?usmUserAuthKey, User), element(?usmUserPrivProtocol, User), element(?usmUserPrivKey, User)}; {_, Name,_,_,_,_,_,_,_,_,_,_,_, RowStatus,_,_} -> ?vdebug("generate_discovery_msg -> " "found user ~p with wrong row status: ~p", [Name, RowStatus]), error(unknownSecurityName); _ -> error(unknownSecurityName) end end, ScopedPduBytes = Message#message.data, {ScopedPduData, MsgPrivParams} = encrypt(ScopedPduBytes, PrivProtocol, PrivKey, SecLevel), UsmSecParams = #usmSecurityParameters{msgAuthoritativeEngineID = SecEngineID, msgAuthoritativeEngineBoots = 0, % Boots, msgAuthoritativeEngineTime = 0, % Time, msgUserName = UserName, msgPrivacyParameters = MsgPrivParams}, Message2 = Message#message{data = ScopedPduData}, authenticate_outgoing(Message2, UsmSecParams, AuthKey, AuthProtocol, SecLevel). %% Ret: {ScopedPDU, MsgPrivParams} - both are already encoded as OCTET STRINGs encrypt(Data, PrivProtocol, PrivKey, SecLevel) -> case snmp_misc:is_priv(SecLevel) of false -> % 3.1.4b ?vtrace("encrypt -> 3.1.4b",[]), {Data, []}; true -> % 3.1.4a ?vtrace("encrypt -> 3.1.4a",[]), case (catch try_encrypt(PrivProtocol, PrivKey, Data)) of {ok, ScopedPduData, MsgPrivParams} -> ?vtrace("encrypt -> encrypted - now encode tag",[]), {snmp_pdus:enc_oct_str_tag(ScopedPduData), MsgPrivParams}; {error, Reason} -> ?vtrace("encrypt -> error: " "~n Reason: ~p", [Reason]), error(Reason); Error -> ?vtrace("encrypt -> other: " "~n Error: ~p", [Error]), error(encryptionError) end end. try_encrypt(?usmNoPrivProtocol, _PrivKey, _Data) -> % 3.1.2 error(unsupportedSecurityLevel); try_encrypt(?usmDESPrivProtocol, PrivKey, Data) -> des_encrypt(PrivKey, Data); try_encrypt(?usmAesCfb128Protocol, PrivKey, Data) -> aes_encrypt(PrivKey, Data). authenticate_outgoing(Message, UsmSecParams, AuthKey, AuthProtocol, SecLevel) -> Message2 = case snmp_misc:is_auth(SecLevel) of true -> auth_out(AuthProtocol, AuthKey, Message, UsmSecParams); false -> set_msg_auth_params(Message, UsmSecParams) end, ?vtrace("authenticate_outgoing -> encode message only",[]), snmp_pdus:enc_message_only(Message2). %%----------------------------------------------------------------- %% Auth and priv algorithms %%----------------------------------------------------------------- auth_in(AuthProtocol, AuthKey, AuthParams, Packet) -> snmp_usm:auth_in(AuthProtocol, AuthKey, AuthParams, Packet). auth_out(AuthProtocol, AuthKey, Message, UsmSecParams) -> snmp_usm:auth_out(AuthProtocol, AuthKey, Message, UsmSecParams). set_msg_auth_params(Message, UsmSecParams) -> snmp_usm:set_msg_auth_params(Message, UsmSecParams, []). des_encrypt(PrivKey, Data) -> snmp_usm:des_encrypt(PrivKey, Data, fun get_des_salt/0). des_decrypt(PrivKey, UsmSecParams, EncData) -> #usmSecurityParameters{msgPrivacyParameters = PrivParms} = UsmSecParams, snmp_usm:des_decrypt(PrivKey, PrivParms, EncData). get_des_salt() -> SaltInt = case catch ets:update_counter(snmp_agent_table, usm_des_salt, 1) of N when N =< 4294967295 -> N; N when is_integer(N) -> % wrap ets:insert(snmp_agent_table, {usm_des_salt, 0}), 0; _ -> % it doesn't exist, initialize {A1,A2,A3} = erlang:now(), random:seed(A1,A2,A3), R = random:uniform(4294967295), ets:insert(snmp_agent_table, {usm_des_salt, R}), R end, EngineBoots = snmp_framework_mib:get_engine_boots(), [?i32(EngineBoots), ?i32(SaltInt)]. aes_encrypt(PrivKey, Data) -> snmp_usm:aes_encrypt(PrivKey, Data, fun get_aes_salt/0). aes_decrypt(PrivKey, UsmSecParams, EncData) -> #usmSecurityParameters{msgPrivacyParameters = PrivParams, msgAuthoritativeEngineTime = EngineTime, msgAuthoritativeEngineBoots = EngineBoots} = UsmSecParams, snmp_usm:aes_decrypt(PrivKey, PrivParams, EncData, EngineBoots, EngineTime). get_aes_salt() -> SaltInt = case catch ets:update_counter(snmp_agent_table, usm_aes_salt, 1) of N when N =< 36893488147419103231 -> N; N when is_integer(N) -> % wrap ets:insert(snmp_agent_table, {usm_aes_salt, 0}), 0; _ -> % it doesn't exist, initialize {A1,A2,A3} = erlang:now(), random:seed(A1,A2,A3), R = random:uniform(36893488147419103231), ets:insert(snmp_agent_table, {usm_aes_salt, R}), R end, [?i64(SaltInt)]. %%----------------------------------------------------------------- %% Discovery wrapper functions %%----------------------------------------------------------------- is_terminating_discovery_enabled() -> snmpa_agent:is_terminating_discovery_enabled(). terminating_discovery_stage2() -> snmpa_agent:terminating_discovery_stage2(). terminating_trigger_username() -> snmpa_agent:terminating_trigger_username(). current_statsNotInTimeWindows_vb() -> #varbind{oid = ?usmStatsNotInTimeWindows_instance, variabletype = 'Counter32', value = get_counter(usmStatsNotInTimeWindows)}. %%----------------------------------------------------------------- %% Future profing... %%----------------------------------------------------------------- get_local_engine_boots(_LocalEngineID) -> snmp_framework_mib:get_engine_boots(). get_local_engine_time(_LocalEngineID) -> snmp_framework_mib:get_engine_time(). %%----------------------------------------------------------------- %% We cache the local values of all non-auth engines we know. %% Keep the values in the snmp_agent_table. %% See section 2.3 of the RFC. %%----------------------------------------------------------------- get_engine_boots(SnmpEngineID) -> case ets:lookup(snmp_agent_table, {usm_eboots, SnmpEngineID}) of [{_Key, Boots}] -> Boots; _ -> 0 end. get_engine_time(SnmpEngineID) -> case ets:lookup(snmp_agent_table, {usm_etime, SnmpEngineID}) of [{_Key, Diff}] -> snmp_misc:now(sec) - Diff; _ -> 0 end. get_engine_latest_time(SnmpEngineID) -> case ets:lookup(snmp_agent_table, {usm_eltime, SnmpEngineID}) of [{_Key, Time}] -> Time; _ -> 0 end. set_engine_boots(SnmpEngineID, EngineBoots) -> ets:insert(snmp_agent_table, {{usm_eboots, SnmpEngineID}, EngineBoots}). set_engine_time(SnmpEngineID, EngineTime) -> Diff = snmp_misc:now(sec) - EngineTime, ets:insert(snmp_agent_table, {{usm_etime, SnmpEngineID}, Diff}). set_engine_latest_time(SnmpEngineID, EngineTime) -> ets:insert(snmp_agent_table, {{usm_eltime, SnmpEngineID}, EngineTime}). %%----------------------------------------------------------------- %% Utility functions %%----------------------------------------------------------------- -spec error(term()) -> no_return(). error(Reason) -> throw({error, Reason}). -spec error(term(), term()) -> no_return(). error(Reason, ErrorInfo) -> throw({error, Reason, ErrorInfo}). -spec error(term(), term(), term()) -> no_return(). error(Variable, Oid, SecName) -> error(Variable, Oid, SecName, []). -spec error(term(), term(), term(), [term()]) -> no_return(). error(Variable, Oid, SecName, Opts) -> Val = inc(Variable), ErrorInfo = {#varbind{oid = Oid, variabletype = 'Counter32', value = Val}, SecName, Opts}, throw({error, Variable, ErrorInfo}). inc(Name) -> ets:update_counter(snmp_agent_table, Name, 1). get_counter(Name) -> case (catch ets:lookup(snmp_agent_table, Name)) of [{_, Val}] -> Val; _ -> 0 end.