1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1999-2019. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20%% AES: RFC 3826 21%% 22 23-module(snmpa_usm). 24 25%% Avoid warning for local function error/1 clashing with autoimported BIF. 26-compile({no_auto_import,[error/1]}). 27%% Avoid warning for local function error/2 clashing with autoimported BIF. 28-compile({no_auto_import,[error/2]}). 29-export([ 30 process_incoming_msg/4, process_incoming_msg/5, 31 generate_outgoing_msg/5, generate_outgoing_msg/6, 32 generate_discovery_msg/4, generate_discovery_msg/5, 33 current_statsNotInTimeWindows_vb/0 34 ]). 35 36-define(SNMP_USE_V3, true). 37-include("snmp_types.hrl"). 38-include("SNMP-USER-BASED-SM-MIB.hrl"). 39-include("SNMP-USM-AES-MIB.hrl"). 40-include("SNMPv2-TC.hrl"). 41 42-define(VMODULE,"A-USM"). 43-include("snmp_verbosity.hrl"). 44-include("snmpa_internal.hrl"). 45 46 47%%----------------------------------------------------------------- 48%% This module implements the User Based Security Model for SNMP, 49%% as defined in rfc2274. 50%%----------------------------------------------------------------- 51 52%% Columns not accessible via SNMP 53-define(usmUserAuthKey, 14). 54-define(usmUserPrivKey, 15). 55 56-define(i32(Int), (Int bsr 24) band 255, (Int bsr 16) band 255, (Int bsr 8) band 255, Int band 255). 57-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). 58 59 60%%----------------------------------------------------------------- 61%% Func: process_incoming_msg(Packet, Data, SecParams, SecLevel) -> 62%% {ok, {SecEngineID, SecName, ScopedPDUBytes, SecData}} | 63%% {error, Reason} | {error, Reason, ErrorInfo} 64%% Return value may be throwed. 65%% Types: Reason -> term() 66%% Purpose: 67%%----------------------------------------------------------------- 68 69process_incoming_msg(Packet, Data, SecParams, SecLevel) -> 70 LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, 71 process_incoming_msg(Packet, Data, SecParams, SecLevel, LocalEngineID). 72 73process_incoming_msg(Packet, Data, SecParams, SecLevel, LocalEngineID) -> 74 TermDiscoEnabled = is_terminating_discovery_enabled(), 75 TermTriggerUsername = terminating_trigger_username(), 76 %% 3.2.1 77 ?vtrace("process_incoming_msg -> check security parms: 3.2.1",[]), 78 UsmSecParams = 79 case catch snmp_pdus:dec_usm_security_parameters(SecParams) of 80 {'EXIT', Reason} -> 81 inc(snmpInASNParseErrs), 82 error({parseError, Reason}, []); 83 Res -> 84 Res 85 end, 86 case UsmSecParams of 87 #usmSecurityParameters{msgAuthoritativeEngineID = MsgAuthEngineID, 88 msgUserName = TermTriggerUsername} when TermDiscoEnabled =:= true -> 89 %% Step 1 discovery message 90 ?vtrace("process_incoming_msg -> [~p] discovery step 1", 91 [TermTriggerUsername]), 92 process_discovery_msg(MsgAuthEngineID, Data, SecLevel); 93 94 #usmSecurityParameters{msgAuthoritativeEngineID = MsgAuthEngineID, 95 msgUserName = MsgUserName} -> 96 ?vlog("process_incoming_msg -> USM security parms: " 97 "~n msgAuthEngineID: ~w" 98 "~n userName: ~p", [MsgAuthEngineID, MsgUserName]), 99 %% 3.2.3 100 ?vtrace("process_incoming_msg -> check engine id: 3.2.3",[]), 101 case snmp_user_based_sm_mib:is_engine_id_known(MsgAuthEngineID) of 102 true -> 103 ok; 104 false -> 105 SecData1 = [MsgUserName], 106 error(usmStatsUnknownEngineIDs, 107 ?usmStatsUnknownEngineIDs_instance, %% OTP-3542 108 undefined, [{sec_data, SecData1}]) 109 end, 110 %% 3.2.4 111 ?vtrace("process_incoming_msg -> retrieve usm user: 3.2.4",[]), 112 UsmUser = 113 case snmp_user_based_sm_mib:get_user(MsgAuthEngineID, 114 MsgUserName) of 115 User when element(?usmUserStatus, User) =:= ?'RowStatus_active' -> 116 User; 117 {_, Name,_,_,_,_,_,_,_,_,_,_,_, RowStatus,_,_} -> 118 ?vdebug("process_incoming_msg -> " 119 "found user ~p with wrong row status: ~p", 120 [Name, RowStatus]), 121 SecData2 = [MsgUserName], 122 error(usmStatsUnknownUserNames, 123 ?usmStatsUnknownUserNames_instance, %% OTP-3542 124 undefined, [{sec_data, SecData2}]); 125 _ -> % undefined or not active user 126 SecData2 = [MsgUserName], 127 error(usmStatsUnknownUserNames, 128 ?usmStatsUnknownUserNames_instance, %% OTP-3542 129 undefined, [{sec_data, SecData2}]) 130 end, 131 SecName = element(?usmUserSecurityName, UsmUser), 132 ?vtrace("process_incoming_msg -> securityName: ~p",[SecName]), 133 %% 3.2.5 - implicit in following checks 134 %% 3.2.6 - 3.2.7 135 ?vtrace("process_incoming_msg -> " 136 "authenticate incoming: 3.2.5 - 3.2.7" 137 "~n ~p",[UsmUser]), 138 DiscoOrPlain = authenticate_incoming(Packet, 139 UsmSecParams, UsmUser, 140 SecLevel, LocalEngineID), 141 %% 3.2.8 142 ?vtrace("process_incoming_msg -> " 143 "decrypt scoped data: 3.2.8",[]), 144 ScopedPDUBytes = 145 decrypt(Data, UsmUser, UsmSecParams, SecLevel), 146 %% 3.2.9 147 %% Means that if AuthKey/PrivKey are changed; 148 %% the old values will be used. 149 ?vtrace("process_incoming_msg -> " 150 "AuthKey/PrivKey are changed - " 151 "use old values: 3.2.9",[]), 152 CachedSecData = {MsgUserName, 153 element(?usmUserAuthProtocol, UsmUser), 154 element(?usmUserPrivProtocol, UsmUser), 155 element(?usmUserAuthKey, UsmUser), 156 element(?usmUserPrivKey, UsmUser)}, 157 {ok, {MsgAuthEngineID, SecName, ScopedPDUBytes, 158 CachedSecData, DiscoOrPlain}} 159 end. 160 161%% Process a step 1 discovery message 162process_discovery_msg(MsgAuthEngineID, Data, SecLevel) -> 163 ?vtrace("process_discovery_msg -> entry with" 164 "~n Data: ~p" 165 "~n SecLevel: ~p", [Data, SecLevel]), 166 case (not snmp_misc:is_priv(SecLevel)) of 167 true -> % noAuthNoPriv 168 ?vtrace("process_discovery_msg -> noAuthNoPriv", []), 169 ScopedPDUBytes = Data, 170 SecData = {"", usmNoAuthProtocol, "", usmNoPrivProtocol, ""}, 171 NewData = {SecData, 172 ?usmStatsUnknownEngineIDs_instance, 173 get_counter(usmStatsUnknownEngineIDs)}, 174 {ok, {MsgAuthEngineID, "", ScopedPDUBytes, NewData, discovery}}; 175 false -> 176 error(usmStatsUnknownEngineIDs, 177 ?usmStatsUnknownEngineIDs_instance, 178 undefined, [{sec_data, ""}]) 179 end. 180 181 182authenticate_incoming(Packet, UsmSecParams, UsmUser, SecLevel, 183 LocalEngineID) -> 184 %% 3.2.6 185 ?vtrace("authenticate_incoming -> 3.2.6", []), 186 AuthProtocol = element(?usmUserAuthProtocol, UsmUser), 187 #usmSecurityParameters{msgAuthoritativeEngineID = MsgAuthEngineID, 188 msgAuthoritativeEngineBoots = MsgAuthEngineBoots, 189 msgAuthoritativeEngineTime = MsgAuthEngineTime, 190 msgAuthenticationParameters = MsgAuthParams} = 191 UsmSecParams, 192 ?vtrace("authenticate_incoming -> Sec params: " 193 "~n MsgAuthEngineID: ~w" 194 "~n MsgAuthEngineBoots: ~p" 195 "~n MsgAuthEngineTime: ~p", 196 [MsgAuthEngineID, MsgAuthEngineBoots, MsgAuthEngineTime]), 197 case snmp_misc:is_auth(SecLevel) of 198 true -> 199 SecName = element(?usmUserSecurityName, UsmUser), 200 case is_auth(AuthProtocol, 201 element(?usmUserAuthKey, UsmUser), 202 MsgAuthParams, 203 Packet, 204 SecName, 205 MsgAuthEngineID, 206 MsgAuthEngineBoots, 207 MsgAuthEngineTime, 208 LocalEngineID) of 209 discovery -> 210 discovery; 211 true -> 212 plain; 213 false -> 214 error(usmStatsWrongDigests, 215 ?usmStatsWrongDigests_instance, % OTP-5464 216 SecName) 217 end; 218 219 false -> % noAuth 220 plain 221 end. 222 223authoritative(SecName, MsgAuthEngineBoots, MsgAuthEngineTime, LocalEngineID) -> 224 ?vtrace("authoritative -> entry with" 225 "~n SecName: ~p" 226 "~n MsgAuthEngineBoots: ~p" 227 "~n MsgAuthEngineTime: ~p", 228 [SecName, MsgAuthEngineBoots, MsgAuthEngineTime]), 229 SnmpEngineBoots = get_local_engine_boots(LocalEngineID), 230 ?vtrace("authoritative -> SnmpEngineBoots: ~p", [SnmpEngineBoots]), 231 SnmpEngineTime = get_local_engine_time(LocalEngineID), 232 ?vtrace("authoritative -> SnmpEngineTime: ~p", [SnmpEngineTime]), 233 InTimeWindow = 234 if 235 SnmpEngineBoots =:= 2147483647 -> false; 236 MsgAuthEngineBoots =/= SnmpEngineBoots -> false; 237 MsgAuthEngineTime + 150 < SnmpEngineTime -> false; 238 MsgAuthEngineTime - 150 > SnmpEngineTime -> false; 239 true -> true 240 end, 241 case InTimeWindow of 242 true -> 243 true; 244 false -> 245 %% OTP-4090 (OTP-3542) 246 ?vinfo("NOT in time window: " 247 "~n SecName: ~p" 248 "~n SnmpEngineBoots: ~p" 249 "~n MsgAuthEngineBoots: ~p" 250 "~n SnmpEngineTime: ~p" 251 "~n MsgAuthEngineTime: ~p", 252 [SecName, 253 SnmpEngineBoots, MsgAuthEngineBoots, 254 SnmpEngineTime, MsgAuthEngineTime]), 255 error(usmStatsNotInTimeWindows, 256 ?usmStatsNotInTimeWindows_instance, 257 SecName, 258 [{securityLevel, 1}]) % authNoPriv 259 end. 260 261non_authoritative(SecName, 262 MsgAuthEngineID, MsgAuthEngineBoots, MsgAuthEngineTime) -> 263 ?vtrace("non_authoritative -> entry with" 264 "~n SecName: ~p" 265 "~n MsgAuthEngineID: ~p" 266 "~n MsgAuthEngineBoots: ~p" 267 "~n MsgAuthEngineTime: ~p", 268 [SecName, 269 MsgAuthEngineID, MsgAuthEngineBoots, MsgAuthEngineTime]), 270 SnmpEngineBoots = get_engine_boots(MsgAuthEngineID), 271 SnmpEngineTime = get_engine_time(MsgAuthEngineID), 272 LatestRecvTime = get_engine_latest_time(MsgAuthEngineID), 273 ?vtrace("non_authoritative -> " 274 "~n SnmpEngineBoots: ~p" 275 "~n SnmpEngineTime: ~p" 276 "~n LatestRecvTime: ~p", 277 [SnmpEngineBoots, SnmpEngineTime, LatestRecvTime]), 278 UpdateLCD = 279 if 280 MsgAuthEngineBoots > SnmpEngineBoots -> true; 281 ((MsgAuthEngineBoots =:= SnmpEngineBoots) andalso 282 (MsgAuthEngineTime > LatestRecvTime)) -> true; 283 true -> false 284 end, 285 case UpdateLCD of 286 true -> %% 3.2.7b1 287 ?vtrace("non_authoritative -> " 288 "update msgAuthoritativeEngineID: 3.2.7b1", 289 []), 290 set_engine_boots(MsgAuthEngineID, MsgAuthEngineBoots), 291 set_engine_time(MsgAuthEngineID, MsgAuthEngineTime), 292 set_engine_latest_time(MsgAuthEngineID, MsgAuthEngineTime); 293 false -> 294 ok 295 end, 296 %% 3.2.7.b2 297 ?vtrace("non_authoritative -> " 298 "check if message is outside time window: 3.2.7b2", []), 299 InTimeWindow = 300 if 301 SnmpEngineBoots =:= 2147483647 -> 302 false; 303 MsgAuthEngineBoots < SnmpEngineBoots -> 304 false; 305 ((MsgAuthEngineBoots =:= SnmpEngineBoots) andalso 306 (MsgAuthEngineTime < (SnmpEngineTime - 150))) -> 307 false; 308 true -> true 309 end, 310 case InTimeWindow of 311 false -> 312 ?vinfo("NOT in time window: " 313 "~n SecName: ~p" 314 "~n SnmpEngineBoots: ~p" 315 "~n MsgAuthEngineBoots: ~p" 316 "~n SnmpEngineTime: ~p" 317 "~n MsgAuthEngineTime: ~p", 318 [SecName, 319 SnmpEngineBoots, MsgAuthEngineBoots, 320 SnmpEngineTime, MsgAuthEngineTime]), 321 error(notInTimeWindow, []); 322 true -> 323 %% If the previous values where all zero's this is the 324 %% second stage discovery message 325 if 326 ((SnmpEngineBoots =:= 0) andalso 327 (SnmpEngineTime =:= 0) andalso 328 (LatestRecvTime =:= 0)) -> 329 ?vtrace("non_authoritative -> " 330 "[maybe] originating discovery stage 2", []), 331 discovery; 332 true -> 333 true 334 end 335 end. 336 337is_auth(?usmNoAuthProtocol, _, _, _, SecName, _, _, _, _) -> % 3.2.5 338 error(usmStatsUnsupportedSecLevels, 339 ?usmStatsUnsupportedSecLevels_instance, SecName); % OTP-5464 340is_auth(AuthProtocol, AuthKey, AuthParams, Packet, SecName, 341 MsgAuthEngineID, MsgAuthEngineBoots, MsgAuthEngineTime, 342 LocalEngineID) -> 343 TermDiscoEnabled = is_terminating_discovery_enabled(), 344 TermDiscoStage2 = terminating_discovery_stage2(), 345 IsAuth = auth_in(AuthProtocol, AuthKey, AuthParams, Packet), 346 ?vtrace("is_auth -> IsAuth: ~p", [IsAuth]), 347 case IsAuth of 348 true -> 349 %% 3.2.7 350 ?vtrace("is_auth -> " 351 "retrieve EngineBoots and EngineTime: 3.2.7",[]), 352 SnmpEngineID = LocalEngineID, 353 ?vtrace("is_auth -> SnmpEngineID: ~p", [SnmpEngineID]), 354 case MsgAuthEngineID of 355 SnmpEngineID when ((MsgAuthEngineBoots =:= 0) andalso 356 (MsgAuthEngineTime =:= 0) andalso 357 (TermDiscoEnabled =:= true) andalso 358 (TermDiscoStage2 =:= discovery)) -> %% 3.2.7a 359 ?vtrace("is_auth -> terminating discovery stage 2 - discovery",[]), 360 discovery; 361 SnmpEngineID when ((MsgAuthEngineBoots =:= 0) andalso 362 (MsgAuthEngineTime =:= 0) andalso 363 (TermDiscoEnabled =:= true) andalso 364 (TermDiscoStage2 =:= plain)) -> %% 3.2.7a 365 ?vtrace("is_auth -> terminating discovery stage 2 - plain",[]), 366 %% This will *always* result in the manager *not* 367 %% beeing in timewindow 368 authoritative(SecName, 369 MsgAuthEngineBoots, MsgAuthEngineTime, 370 LocalEngineID); 371 372 SnmpEngineID -> %% 3.2.7a 373 ?vtrace("is_auth -> we are authoritative: 3.2.7a", []), 374 authoritative(SecName, 375 MsgAuthEngineBoots, MsgAuthEngineTime, 376 LocalEngineID); 377 378 _ -> %% 3.2.7b - we're non-authoritative 379 ?vtrace("is_auth -> we are non-authoritative: 3.2.7b",[]), 380 non_authoritative(SecName, 381 MsgAuthEngineID, 382 MsgAuthEngineBoots, MsgAuthEngineTime) 383 end; 384 385 false -> 386 false 387 end. 388 389 390decrypt(Data, UsmUser, UsmSecParams, SecLevel) -> 391 case snmp_misc:is_priv(SecLevel) of 392 true -> 393 do_decrypt(Data, UsmUser, UsmSecParams); 394 false -> 395 Data 396 end. 397 398do_decrypt(Data, UsmUser, UsmSecParams) -> 399 EncryptedPDU = snmp_pdus:dec_scoped_pdu_data(Data), 400 SecName = element(?usmUserSecurityName, UsmUser), 401 PrivP = element(?usmUserPrivProtocol, UsmUser), 402 PrivKey = element(?usmUserPrivKey, UsmUser), 403 ?vtrace("do_decrypt -> try decrypt with: " 404 "~n SecName: ~p" 405 "~n PrivP: ~p", [SecName, PrivP]), 406 try_decrypt(PrivP, PrivKey, UsmSecParams, EncryptedPDU, SecName). 407 408try_decrypt(?usmNoPrivProtocol, _, _, _, SecName) -> % 3.2.5 409 error(usmStatsUnsupportedSecLevels, 410 ?usmStatsUnsupportedSecLevels_instance, SecName); % OTP-5464 411try_decrypt(?usmDESPrivProtocol, 412 PrivKey, UsmSecParams, EncryptedPDU, SecName) -> 413 case (catch des_decrypt(PrivKey, UsmSecParams, EncryptedPDU)) of 414 {ok, DecryptedData} -> 415 DecryptedData; 416 Error -> 417 ?vlog("try_decrypt -> failed DES decrypt" 418 "~n Error: ~p", [Error]), 419 error(usmStatsDecryptionErrors, 420 ?usmStatsDecryptionErrors_instance, % OTP-5464 421 SecName) 422 end; 423try_decrypt(?usmAesCfb128Protocol, 424 PrivKey, UsmSecParams, EncryptedPDU, SecName) -> 425 case (catch aes_decrypt(PrivKey, UsmSecParams, EncryptedPDU)) of 426 {ok, DecryptedData} -> 427 DecryptedData; 428 Error -> 429 ?vlog("try_decrypt -> failed AES decrypt" 430 "~n Error: ~p", [Error]), 431 error(usmStatsDecryptionErrors, 432 ?usmStatsDecryptionErrors_instance, % OTP-5464 433 SecName) 434 end. 435 436 437generate_outgoing_msg(Message, SecEngineID, SecName, SecData, SecLevel) -> 438 LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, 439 generate_outgoing_msg(Message, SecEngineID, SecName, SecData, SecLevel, 440 LocalEngineID). 441 442generate_outgoing_msg(Message, SecEngineID, SecName, SecData, SecLevel, 443 LocalEngineID) -> 444 %% 3.1.1 445 ?vtrace("generate_outgoing_msg -> [3.1.1] entry with" 446 "~n SecEngineID: ~p" 447 "~n SecName: ~p" 448 "~n SecLevel: ~w" 449 "~n LocalEngineID: ~p", 450 [SecEngineID, SecName, SecLevel, LocalEngineID]), 451 {UserName, AuthProtocol, PrivProtocol, AuthKey, PrivKey} = 452 case SecData of 453 [] -> % 3.1.1b 454 %% Not a response - read from LCD 455 case snmp_user_based_sm_mib:get_user_from_security_name( 456 SecEngineID, SecName) of 457 User when element(?usmUserStatus, User) =:= 458 ?'RowStatus_active' -> 459 {element(?usmUserName, User), 460 element(?usmUserAuthProtocol, User), 461 element(?usmUserPrivProtocol, User), 462 element(?usmUserAuthKey, User), 463 element(?usmUserPrivKey, User)}; 464 {_, Name,_,_,_,_,_,_,_,_,_,_,_, RowStatus,_,_} -> 465 ?vdebug("generate_outgoing_msg -> " 466 "found not active user ~p: ~p", 467 [Name, RowStatus]), 468 error(unknownSecurityName); 469 _ -> 470 error(unknownSecurityName) 471 end; 472 [MsgUserName] -> 473 %% This means the user at the engine is unknown 474 {MsgUserName, ?usmNoAuthProtocol, ?usmNoPrivProtocol, "", ""}; 475 _ -> % 3.1.1a 476 SecData 477 end, 478 %% 3.1.6 479 SnmpEngineID = LocalEngineID, 480 ?vtrace("generate_outgoing_msg -> SnmpEngineID: ~p [3.1.6]", 481 [SnmpEngineID]), 482 {MsgAuthEngineBoots, MsgAuthEngineTime} = 483 case snmp_misc:is_auth(SecLevel) of 484 false when SecData =:= [] -> % not a response 485 {0, 0}; 486 false when UserName =:= "" -> % reply (report) to discovery step 1 487 {0, 0}; 488 true when SecEngineID =/= SnmpEngineID -> 489 {get_engine_boots(SecEngineID), 490 get_engine_time(SecEngineID)}; 491 _ -> 492 {get_local_engine_boots(SnmpEngineID), 493 get_local_engine_time(SnmpEngineID)} 494 end, 495 %% 3.1.4 496 ?vtrace("generate_outgoing_msg -> [3.1.4]" 497 "~n UserName: ~p" 498 "~n AuthProtocol: ~p" 499 "~n PrivProtocol: ~p", 500 [UserName, AuthProtocol, PrivProtocol]), 501 ScopedPduBytes = Message#message.data, 502 {ScopedPduData, MsgPrivParams} = 503 encrypt(ScopedPduBytes, PrivProtocol, PrivKey, SecLevel, 504 MsgAuthEngineBoots, MsgAuthEngineTime), 505 %% 3.1.5 - 3.1.7 506 ?vtrace("generate_outgoing_msg -> [3.1.5 - 3.1.7]",[]), 507 UsmSecParams = 508 #usmSecurityParameters{msgAuthoritativeEngineID = SecEngineID, 509 msgAuthoritativeEngineBoots = MsgAuthEngineBoots, 510 msgAuthoritativeEngineTime = MsgAuthEngineTime, 511 msgUserName = UserName, 512 msgPrivacyParameters = MsgPrivParams}, 513 Message2 = Message#message{data = ScopedPduData}, 514 %% 3.1.8 515 ?vtrace("generate_outgoing_msg -> [3.1.8]",[]), 516 authenticate_outgoing(Message2, UsmSecParams, 517 AuthKey, AuthProtocol, SecLevel). 518 519 520generate_discovery_msg(Message, SecEngineID, SecName, SecLevel) -> 521 generate_discovery_msg(Message, SecEngineID, SecName, SecLevel, ""). 522 523generate_discovery_msg(Message, 524 SecEngineID, SecName, SecLevel, 525 InitialUserName) -> 526 ?vtrace("generate_discovery_msg -> entry with" 527 "~n SecEngineID: ~p" 528 "~n SecName: ~p" 529 "~n SecLevel: ~p" 530 "~n InitialUserName: ~p", 531 [SecEngineID, SecName, SecLevel, InitialUserName]), 532 {UserName, AuthProtocol, AuthKey, PrivProtocol, PrivKey} = 533 case SecEngineID of 534 "" -> 535 %% Discovery step 1 536 %% Nothing except the user name will be used in this 537 %% tuple in this step, but since we need some values, 538 %% we fill in proper ones just in case 539 %% {"initial", usmNoAuthProtocol, "", usmNoPrivProtocol, ""}; 540 %% {"", usmNoAuthProtocol, "", usmNoPrivProtocol, ""}; 541 {InitialUserName, 542 usmNoAuthProtocol, "", usmNoPrivProtocol, ""}; 543 544 _ -> 545 %% Discovery step 2 546 case snmp_user_based_sm_mib:get_user_from_security_name( 547 SecEngineID, SecName) of 548 User when element(?usmUserStatus, User) =:= 549 ?'RowStatus_active' -> 550 {element(?usmUserName, User), 551 element(?usmUserAuthProtocol, User), 552 element(?usmUserAuthKey, User), 553 element(?usmUserPrivProtocol, User), 554 element(?usmUserPrivKey, User)}; 555 {_, Name,_,_,_,_,_,_,_,_,_,_,_, RowStatus,_,_} -> 556 ?vdebug("generate_discovery_msg -> " 557 "found user ~p with wrong row status: ~p", 558 [Name, RowStatus]), 559 error(unknownSecurityName); 560 _ -> 561 error(unknownSecurityName) 562 end 563 end, 564 ScopedPduBytes = Message#message.data, 565 MsgAuthEngineBoots = 0, 566 MsgAuthEngineTime = 0, 567 {ScopedPduData, MsgPrivParams} = 568 encrypt(ScopedPduBytes, PrivProtocol, PrivKey, SecLevel, 569 MsgAuthEngineBoots, MsgAuthEngineTime), 570 UsmSecParams = 571 #usmSecurityParameters{msgAuthoritativeEngineID = SecEngineID, 572 msgAuthoritativeEngineBoots = % Boots 573 MsgAuthEngineBoots, 574 msgAuthoritativeEngineTime = % Time 575 MsgAuthEngineTime, 576 msgUserName = UserName, 577 msgPrivacyParameters = MsgPrivParams}, 578 Message2 = Message#message{data = ScopedPduData}, 579 authenticate_outgoing(Message2, UsmSecParams, 580 AuthKey, AuthProtocol, SecLevel). 581 582 583%% Ret: {ScopedPDU, MsgPrivParams} - both are already encoded as OCTET STRINGs 584encrypt(Data, PrivProtocol, PrivKey, SecLevel, EngineBoots, EngineTime) -> 585 case snmp_misc:is_priv(SecLevel) of 586 false -> % 3.1.4b 587 ?vtrace("encrypt -> 3.1.4b",[]), 588 {Data, []}; 589 true -> % 3.1.4a 590 ?vtrace("encrypt -> 3.1.4a",[]), 591 case (catch try_encrypt(PrivProtocol, PrivKey, Data, 592 EngineBoots, EngineTime)) of 593 {ok, ScopedPduData, MsgPrivParams} -> 594 ?vtrace("encrypt -> encrypted - now encode tag",[]), 595 {snmp_pdus:enc_oct_str_tag(ScopedPduData), MsgPrivParams}; 596 {error, Reason} -> 597 ?vtrace("encrypt -> error: " 598 "~n Reason: ~p", [Reason]), 599 error(Reason); 600 Error -> 601 ?vtrace("encrypt -> other: " 602 "~n Error: ~p", [Error]), 603 error(encryptionError) 604 end 605 end. 606 607try_encrypt( 608 ?usmNoPrivProtocol, _PrivKey, _Data, _EngineBoots, _EngineTime) -> % 3.1.2 609 error(unsupportedSecurityLevel); 610try_encrypt(?usmDESPrivProtocol, PrivKey, Data, _EngineBoots, _EngineTime) -> 611 des_encrypt(PrivKey, Data); 612try_encrypt(?usmAesCfb128Protocol, PrivKey, Data, EngineBoots, EngineTime) -> 613 aes_encrypt(PrivKey, Data, EngineBoots, EngineTime). 614 615 616authenticate_outgoing(Message, UsmSecParams, 617 AuthKey, AuthProtocol, SecLevel) -> 618 Message2 = 619 case snmp_misc:is_auth(SecLevel) of 620 true -> 621 auth_out(AuthProtocol, AuthKey, Message, UsmSecParams); 622 false -> 623 set_msg_auth_params(Message, UsmSecParams) 624 end, 625 ?vtrace("authenticate_outgoing -> encode message only",[]), 626 snmp_pdus:enc_message_only(Message2). 627 628 629%%----------------------------------------------------------------- 630%% Auth and priv algorithms 631%%----------------------------------------------------------------- 632auth_in(AuthProtocol, AuthKey, AuthParams, Packet) -> 633 snmp_usm:auth_in(AuthProtocol, AuthKey, AuthParams, Packet). 634 635auth_out(AuthProtocol, AuthKey, Message, UsmSecParams) -> 636 snmp_usm:auth_out(AuthProtocol, AuthKey, Message, UsmSecParams). 637 638set_msg_auth_params(Message, UsmSecParams) -> 639 snmp_usm:set_msg_auth_params(Message, UsmSecParams, []). 640 641des_encrypt(PrivKey, Data) -> 642 snmp_usm:des_encrypt(PrivKey, Data, fun get_des_salt/0). 643 644des_decrypt(PrivKey, UsmSecParams, EncData) -> 645 #usmSecurityParameters{msgPrivacyParameters = PrivParms} = UsmSecParams, 646 snmp_usm:des_decrypt(PrivKey, PrivParms, EncData). 647 648get_des_salt() -> 649 SaltInt = 650 case catch ets:update_counter(snmp_agent_table, usm_des_salt, 1) of 651 N when N =< 4294967295 -> 652 N; 653 N when is_integer(N) -> % wrap 654 ets:insert(snmp_agent_table, {usm_des_salt, 0}), 655 0; 656 _ -> % it doesn't exist, initialize 657 ?SNMP_RAND_SEED(), 658 %% rand:seed(exrop, 659 %% {erlang:phash2([node()]), 660 %% erlang:monotonic_time(), 661 %% erlang:unique_integer()}), 662 R = rand:uniform(4294967295), 663 ets:insert(snmp_agent_table, {usm_des_salt, R}), 664 R 665 end, 666 EngineBoots = snmp_framework_mib:get_engine_boots(), 667 [?i32(EngineBoots), ?i32(SaltInt)]. 668 669aes_encrypt(PrivKey, Data, EngineBoots, EngineTime) -> 670 snmp_usm:aes_encrypt(PrivKey, Data, fun get_aes_salt/0, 671 EngineBoots, EngineTime). 672 673aes_decrypt(PrivKey, UsmSecParams, EncData) -> 674 #usmSecurityParameters{msgPrivacyParameters = PrivParams, 675 msgAuthoritativeEngineTime = EngineTime, 676 msgAuthoritativeEngineBoots = EngineBoots} = 677 UsmSecParams, 678 snmp_usm:aes_decrypt(PrivKey, PrivParams, EncData, 679 EngineBoots, EngineTime). 680 681get_aes_salt() -> 682 SaltInt = 683 case catch ets:update_counter(snmp_agent_table, usm_aes_salt, 1) of 684 N when N =< 36893488147419103231 -> 685 N; 686 N when is_integer(N) -> % wrap 687 ets:insert(snmp_agent_table, {usm_aes_salt, 0}), 688 0; 689 _ -> % it doesn't exist, initialize 690 ?SNMP_RAND_SEED(), 691 %% rand:seed(exrop, 692 %% {erlang:phash2([node()]), 693 %% erlang:monotonic_time(), 694 %% erlang:unique_integer()}), 695 R = rand:uniform(36893488147419103231), 696 ets:insert(snmp_agent_table, {usm_aes_salt, R}), 697 R 698 end, 699 [?i64(SaltInt)]. 700 701 702 703%%----------------------------------------------------------------- 704%% Discovery wrapper functions 705%%----------------------------------------------------------------- 706 707is_terminating_discovery_enabled() -> 708 snmpa_agent:is_terminating_discovery_enabled(). 709 710terminating_discovery_stage2() -> 711 snmpa_agent:terminating_discovery_stage2(). 712 713terminating_trigger_username() -> 714 snmpa_agent:terminating_trigger_username(). 715 716current_statsNotInTimeWindows_vb() -> 717 #varbind{oid = ?usmStatsNotInTimeWindows_instance, 718 variabletype = 'Counter32', 719 value = get_counter(usmStatsNotInTimeWindows)}. 720 721 722 723%%----------------------------------------------------------------- 724%% Future profing... 725%%----------------------------------------------------------------- 726 727get_local_engine_boots(_LocalEngineID) -> 728 snmp_framework_mib:get_engine_boots(). 729 730get_local_engine_time(_LocalEngineID) -> 731 snmp_framework_mib:get_engine_time(). 732 733 734 735%%----------------------------------------------------------------- 736%% We cache the local values of all non-auth engines we know. 737%% Keep the values in the snmp_agent_table. 738%% See section 2.3 of the RFC. 739%%----------------------------------------------------------------- 740get_engine_boots(SnmpEngineID) -> 741 case ets:lookup(snmp_agent_table, {usm_eboots, SnmpEngineID}) of 742 [{_Key, Boots}] -> Boots; 743 _ -> 0 744 end. 745 746get_engine_time(SnmpEngineID) -> 747 case ets:lookup(snmp_agent_table, {usm_etime, SnmpEngineID}) of 748 [{_Key, Diff}] -> snmp_misc:now(sec) - Diff; 749 _ -> 0 750 end. 751 752get_engine_latest_time(SnmpEngineID) -> 753 case ets:lookup(snmp_agent_table, {usm_eltime, SnmpEngineID}) of 754 [{_Key, Time}] -> Time; 755 _ -> 0 756 end. 757 758 759set_engine_boots(SnmpEngineID, EngineBoots) -> 760 ets:insert(snmp_agent_table, {{usm_eboots, SnmpEngineID}, EngineBoots}). 761 762set_engine_time(SnmpEngineID, EngineTime) -> 763 Diff = snmp_misc:now(sec) - EngineTime, 764 ets:insert(snmp_agent_table, {{usm_etime, SnmpEngineID}, Diff}). 765 766set_engine_latest_time(SnmpEngineID, EngineTime) -> 767 ets:insert(snmp_agent_table, {{usm_eltime, SnmpEngineID}, EngineTime}). 768 769 770%%----------------------------------------------------------------- 771%% Utility functions 772%%----------------------------------------------------------------- 773-spec error(term()) -> no_return(). 774error(Reason) -> 775 throw({error, Reason}). 776 777-spec error(term(), term()) -> no_return(). 778error(Reason, ErrorInfo) -> 779 throw({error, Reason, ErrorInfo}). 780 781-spec error(term(), term(), term()) -> no_return(). 782error(Variable, Oid, SecName) -> 783 error(Variable, Oid, SecName, []). 784 785-spec error(term(), term(), term(), [term()]) -> no_return(). 786error(Variable, Oid, SecName, Opts) -> 787 Val = inc(Variable), 788 ErrorInfo = {#varbind{oid = Oid, 789 variabletype = 'Counter32', 790 value = Val}, 791 SecName, 792 Opts}, 793 throw({error, Variable, ErrorInfo}). 794 795inc(Name) -> ets:update_counter(snmp_agent_table, Name, 1). 796 797get_counter(Name) -> 798 case (catch ets:lookup(snmp_agent_table, Name)) of 799 [{_, Val}] -> 800 Val; 801 _ -> 802 0 803 end. 804