aboutsummaryrefslogtreecommitdiffstats
path: root/lib/snmp/src/agent/snmpa_usm.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/snmp/src/agent/snmpa_usm.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/snmp/src/agent/snmpa_usm.erl')
-rw-r--r--lib/snmp/src/agent/snmpa_usm.erl745
1 files changed, 745 insertions, 0 deletions
diff --git a/lib/snmp/src/agent/snmpa_usm.erl b/lib/snmp/src/agent/snmpa_usm.erl
new file mode 100644
index 0000000000..a8c395534f
--- /dev/null
+++ b/lib/snmp/src/agent/snmpa_usm.erl
@@ -0,0 +1,745 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2009. 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).
+
+-export([
+ process_incoming_msg/4,
+ generate_outgoing_msg/5,
+ 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").
+
+
+%%-----------------------------------------------------------------
+%% 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) ->
+ 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),
+ %% 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) ->
+ %% 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) of
+ discovery ->
+ discovery;
+ true ->
+ plain;
+ false ->
+ error(usmStatsWrongDigests,
+ ?usmStatsWrongDigests_instance, % OTP-5464
+ SecName)
+ end;
+
+ false -> % noAuth
+ plain
+ end.
+
+authoritative(SecName, MsgAuthEngineBoots, MsgAuthEngineTime) ->
+ ?vtrace("authoritative -> entry with"
+ "~n SecName: ~p"
+ "~n MsgAuthEngineBoots: ~p"
+ "~n MsgAuthEngineTime: ~p",
+ [SecName, MsgAuthEngineBoots, MsgAuthEngineTime]),
+ SnmpEngineBoots = snmp_framework_mib:get_engine_boots(),
+ ?vtrace("authoritative -> SnmpEngineBoots: ~p", [SnmpEngineBoots]),
+ SnmpEngineTime = snmp_framework_mib:get_engine_time(),
+ ?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) ->
+ 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 = snmp_framework_mib:get_engine_id(),
+ ?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);
+
+ SnmpEngineID -> %% 3.2.7a
+ ?vtrace("is_auth -> we are authoritative: 3.2.7a", []),
+ authoritative(SecName,
+ MsgAuthEngineBoots, MsgAuthEngineTime);
+
+ _ -> %% 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(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(usmStatsDecryptionErrors,
+ ?usmStatsDecryptionErrors_instance, % OTP-5464
+ SecName)
+ end.
+
+
+generate_outgoing_msg(Message, SecEngineID, SecName, SecData, SecLevel) ->
+ %% 3.1.1
+ ?vtrace("generate_outgoing_msg -> [3.1.1] entry with"
+ "~n SecEngineID: ~p"
+ "~n SecName: ~p"
+ "~n SecLevel: ~w",
+ [SecEngineID, SecName, SecLevel]),
+ {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 user ~p with wrong row status: ~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 = snmp_framework_mib:get_engine_id(),
+ ?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)};
+ _ ->
+ {snmp_framework_mib:get_engine_boots(),
+ snmp_framework_mib:get_engine_time()}
+ 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 -> encode tag",[]),
+ {snmp_pdus:enc_oct_str_tag(ScopedPduData), MsgPrivParams};
+ {error, Reason} ->
+ error(Reason);
+ _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)}.
+
+
+%%-----------------------------------------------------------------
+%% 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
+%%-----------------------------------------------------------------
+error(Reason) ->
+ throw({error, Reason}).
+
+error(Reason, ErrorInfo) ->
+ throw({error, Reason, ErrorInfo}).
+
+error(Variable, Oid, SecName) ->
+ error(Variable, Oid, SecName, []).
+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.
+
+
+
+
+