1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1999-2015. 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.4 479 ?vtrace("generate_outgoing_msg -> [3.1.4]" 480 "~n UserName: ~p" 481 "~n AuthProtocol: ~p" 482 "~n PrivProtocol: ~p", 483 [UserName, AuthProtocol, PrivProtocol]), 484 ScopedPduBytes = Message#message.data, 485 {ScopedPduData, MsgPrivParams} = 486 encrypt(ScopedPduBytes, PrivProtocol, PrivKey, SecLevel), 487 SnmpEngineID = LocalEngineID, 488 ?vtrace("generate_outgoing_msg -> SnmpEngineID: ~p [3.1.6]", 489 [SnmpEngineID]), 490 %% 3.1.6 491 {MsgAuthEngineBoots, MsgAuthEngineTime} = 492 case snmp_misc:is_auth(SecLevel) of 493 false when SecData =:= [] -> % not a response 494 {0, 0}; 495 false when UserName =:= "" -> % reply (report) to discovery step 1 496 {0, 0}; 497 true when SecEngineID =/= SnmpEngineID -> 498 {get_engine_boots(SecEngineID), 499 get_engine_time(SecEngineID)}; 500 _ -> 501 {get_local_engine_boots(SnmpEngineID), 502 get_local_engine_time(SnmpEngineID)} 503 end, 504 %% 3.1.5 - 3.1.7 505 ?vtrace("generate_outgoing_msg -> [3.1.5 - 3.1.7]",[]), 506 UsmSecParams = 507 #usmSecurityParameters{msgAuthoritativeEngineID = SecEngineID, 508 msgAuthoritativeEngineBoots = MsgAuthEngineBoots, 509 msgAuthoritativeEngineTime = MsgAuthEngineTime, 510 msgUserName = UserName, 511 msgPrivacyParameters = MsgPrivParams}, 512 Message2 = Message#message{data = ScopedPduData}, 513 %% 3.1.8 514 ?vtrace("generate_outgoing_msg -> [3.1.8]",[]), 515 authenticate_outgoing(Message2, UsmSecParams, 516 AuthKey, AuthProtocol, SecLevel). 517 518 519generate_discovery_msg(Message, SecEngineID, SecName, SecLevel) -> 520 generate_discovery_msg(Message, SecEngineID, SecName, SecLevel, ""). 521 522generate_discovery_msg(Message, 523 SecEngineID, SecName, SecLevel, 524 InitialUserName) -> 525 ?vtrace("generate_discovery_msg -> entry with" 526 "~n SecEngineID: ~p" 527 "~n SecName: ~p" 528 "~n SecLevel: ~p" 529 "~n InitialUserName: ~p", 530 [SecEngineID, SecName, SecLevel, InitialUserName]), 531 {UserName, AuthProtocol, AuthKey, PrivProtocol, PrivKey} = 532 case SecEngineID of 533 "" -> 534 %% Discovery step 1 535 %% Nothing except the user name will be used in this 536 %% tuple in this step, but since we need some values, 537 %% we fill in proper ones just in case 538 %% {"initial", usmNoAuthProtocol, "", usmNoPrivProtocol, ""}; 539 %% {"", usmNoAuthProtocol, "", usmNoPrivProtocol, ""}; 540 {InitialUserName, 541 usmNoAuthProtocol, "", usmNoPrivProtocol, ""}; 542 543 _ -> 544 %% Discovery step 2 545 case snmp_user_based_sm_mib:get_user_from_security_name( 546 SecEngineID, SecName) of 547 User when element(?usmUserStatus, User) =:= 548 ?'RowStatus_active' -> 549 {element(?usmUserName, User), 550 element(?usmUserAuthProtocol, User), 551 element(?usmUserAuthKey, User), 552 element(?usmUserPrivProtocol, User), 553 element(?usmUserPrivKey, User)}; 554 {_, Name,_,_,_,_,_,_,_,_,_,_,_, RowStatus,_,_} -> 555 ?vdebug("generate_discovery_msg -> " 556 "found user ~p with wrong row status: ~p", 557 [Name, RowStatus]), 558 error(unknownSecurityName); 559 _ -> 560 error(unknownSecurityName) 561 end 562 end, 563 ScopedPduBytes = Message#message.data, 564 {ScopedPduData, MsgPrivParams} = 565 encrypt(ScopedPduBytes, PrivProtocol, PrivKey, SecLevel), 566 UsmSecParams = 567 #usmSecurityParameters{msgAuthoritativeEngineID = SecEngineID, 568 msgAuthoritativeEngineBoots = 0, % Boots, 569 msgAuthoritativeEngineTime = 0, % Time, 570 msgUserName = UserName, 571 msgPrivacyParameters = MsgPrivParams}, 572 Message2 = Message#message{data = ScopedPduData}, 573 authenticate_outgoing(Message2, UsmSecParams, 574 AuthKey, AuthProtocol, SecLevel). 575 576 577%% Ret: {ScopedPDU, MsgPrivParams} - both are already encoded as OCTET STRINGs 578encrypt(Data, PrivProtocol, PrivKey, SecLevel) -> 579 case snmp_misc:is_priv(SecLevel) of 580 false -> % 3.1.4b 581 ?vtrace("encrypt -> 3.1.4b",[]), 582 {Data, []}; 583 true -> % 3.1.4a 584 ?vtrace("encrypt -> 3.1.4a",[]), 585 case (catch try_encrypt(PrivProtocol, PrivKey, Data)) of 586 {ok, ScopedPduData, MsgPrivParams} -> 587 ?vtrace("encrypt -> encrypted - now encode tag",[]), 588 {snmp_pdus:enc_oct_str_tag(ScopedPduData), MsgPrivParams}; 589 {error, Reason} -> 590 ?vtrace("encrypt -> error: " 591 "~n Reason: ~p", [Reason]), 592 error(Reason); 593 Error -> 594 ?vtrace("encrypt -> other: " 595 "~n Error: ~p", [Error]), 596 error(encryptionError) 597 end 598 end. 599 600try_encrypt(?usmNoPrivProtocol, _PrivKey, _Data) -> % 3.1.2 601 error(unsupportedSecurityLevel); 602try_encrypt(?usmDESPrivProtocol, PrivKey, Data) -> 603 des_encrypt(PrivKey, Data); 604try_encrypt(?usmAesCfb128Protocol, PrivKey, Data) -> 605 aes_encrypt(PrivKey, Data). 606 607 608authenticate_outgoing(Message, UsmSecParams, 609 AuthKey, AuthProtocol, SecLevel) -> 610 Message2 = 611 case snmp_misc:is_auth(SecLevel) of 612 true -> 613 auth_out(AuthProtocol, AuthKey, Message, UsmSecParams); 614 false -> 615 set_msg_auth_params(Message, UsmSecParams) 616 end, 617 ?vtrace("authenticate_outgoing -> encode message only",[]), 618 snmp_pdus:enc_message_only(Message2). 619 620 621%%----------------------------------------------------------------- 622%% Auth and priv algorithms 623%%----------------------------------------------------------------- 624auth_in(AuthProtocol, AuthKey, AuthParams, Packet) -> 625 snmp_usm:auth_in(AuthProtocol, AuthKey, AuthParams, Packet). 626 627auth_out(AuthProtocol, AuthKey, Message, UsmSecParams) -> 628 snmp_usm:auth_out(AuthProtocol, AuthKey, Message, UsmSecParams). 629 630set_msg_auth_params(Message, UsmSecParams) -> 631 snmp_usm:set_msg_auth_params(Message, UsmSecParams, []). 632 633des_encrypt(PrivKey, Data) -> 634 snmp_usm:des_encrypt(PrivKey, Data, fun get_des_salt/0). 635 636des_decrypt(PrivKey, UsmSecParams, EncData) -> 637 #usmSecurityParameters{msgPrivacyParameters = PrivParms} = UsmSecParams, 638 snmp_usm:des_decrypt(PrivKey, PrivParms, EncData). 639 640get_des_salt() -> 641 SaltInt = 642 case catch ets:update_counter(snmp_agent_table, usm_des_salt, 1) of 643 N when N =< 4294967295 -> 644 N; 645 N when is_integer(N) -> % wrap 646 ets:insert(snmp_agent_table, {usm_des_salt, 0}), 647 0; 648 _ -> % it doesn't exist, initialize 649 random:seed(erlang:phash2([node()]), 650 erlang:monotonic_time(), 651 erlang:unique_integer()), 652 R = random:uniform(4294967295), 653 ets:insert(snmp_agent_table, {usm_des_salt, R}), 654 R 655 end, 656 EngineBoots = snmp_framework_mib:get_engine_boots(), 657 [?i32(EngineBoots), ?i32(SaltInt)]. 658 659aes_encrypt(PrivKey, Data) -> 660 EngineBoots = snmp_framework_mib:get_engine_boots(), 661 EngineTime = snmp_framework_mib:get_engine_time(), 662 snmp_usm:aes_encrypt(PrivKey, Data, fun get_aes_salt/0, 663 EngineBoots, EngineTime). 664 665aes_decrypt(PrivKey, UsmSecParams, EncData) -> 666 #usmSecurityParameters{msgPrivacyParameters = PrivParams, 667 msgAuthoritativeEngineTime = EngineTime, 668 msgAuthoritativeEngineBoots = EngineBoots} = 669 UsmSecParams, 670 snmp_usm:aes_decrypt(PrivKey, PrivParams, EncData, 671 EngineBoots, EngineTime). 672 673get_aes_salt() -> 674 SaltInt = 675 case catch ets:update_counter(snmp_agent_table, usm_aes_salt, 1) of 676 N when N =< 36893488147419103231 -> 677 N; 678 N when is_integer(N) -> % wrap 679 ets:insert(snmp_agent_table, {usm_aes_salt, 0}), 680 0; 681 _ -> % it doesn't exist, initialize 682 random:seed(erlang:phash2([node()]), 683 erlang:monotonic_time(), 684 erlang:unique_integer()), 685 R = random:uniform(36893488147419103231), 686 ets:insert(snmp_agent_table, {usm_aes_salt, R}), 687 R 688 end, 689 [?i64(SaltInt)]. 690 691 692 693%%----------------------------------------------------------------- 694%% Discovery wrapper functions 695%%----------------------------------------------------------------- 696 697is_terminating_discovery_enabled() -> 698 snmpa_agent:is_terminating_discovery_enabled(). 699 700terminating_discovery_stage2() -> 701 snmpa_agent:terminating_discovery_stage2(). 702 703terminating_trigger_username() -> 704 snmpa_agent:terminating_trigger_username(). 705 706current_statsNotInTimeWindows_vb() -> 707 #varbind{oid = ?usmStatsNotInTimeWindows_instance, 708 variabletype = 'Counter32', 709 value = get_counter(usmStatsNotInTimeWindows)}. 710 711 712 713%%----------------------------------------------------------------- 714%% Future profing... 715%%----------------------------------------------------------------- 716 717get_local_engine_boots(_LocalEngineID) -> 718 snmp_framework_mib:get_engine_boots(). 719 720get_local_engine_time(_LocalEngineID) -> 721 snmp_framework_mib:get_engine_time(). 722 723 724 725%%----------------------------------------------------------------- 726%% We cache the local values of all non-auth engines we know. 727%% Keep the values in the snmp_agent_table. 728%% See section 2.3 of the RFC. 729%%----------------------------------------------------------------- 730get_engine_boots(SnmpEngineID) -> 731 case ets:lookup(snmp_agent_table, {usm_eboots, SnmpEngineID}) of 732 [{_Key, Boots}] -> Boots; 733 _ -> 0 734 end. 735 736get_engine_time(SnmpEngineID) -> 737 case ets:lookup(snmp_agent_table, {usm_etime, SnmpEngineID}) of 738 [{_Key, Diff}] -> snmp_misc:now(sec) - Diff; 739 _ -> 0 740 end. 741 742get_engine_latest_time(SnmpEngineID) -> 743 case ets:lookup(snmp_agent_table, {usm_eltime, SnmpEngineID}) of 744 [{_Key, Time}] -> Time; 745 _ -> 0 746 end. 747 748 749set_engine_boots(SnmpEngineID, EngineBoots) -> 750 ets:insert(snmp_agent_table, {{usm_eboots, SnmpEngineID}, EngineBoots}). 751 752set_engine_time(SnmpEngineID, EngineTime) -> 753 Diff = snmp_misc:now(sec) - EngineTime, 754 ets:insert(snmp_agent_table, {{usm_etime, SnmpEngineID}, Diff}). 755 756set_engine_latest_time(SnmpEngineID, EngineTime) -> 757 ets:insert(snmp_agent_table, {{usm_eltime, SnmpEngineID}, EngineTime}). 758 759 760%%----------------------------------------------------------------- 761%% Utility functions 762%%----------------------------------------------------------------- 763-spec error(term()) -> no_return(). 764error(Reason) -> 765 throw({error, Reason}). 766 767-spec error(term(), term()) -> no_return(). 768error(Reason, ErrorInfo) -> 769 throw({error, Reason, ErrorInfo}). 770 771-spec error(term(), term(), term()) -> no_return(). 772error(Variable, Oid, SecName) -> 773 error(Variable, Oid, SecName, []). 774 775-spec error(term(), term(), term(), [term()]) -> no_return(). 776error(Variable, Oid, SecName, Opts) -> 777 Val = inc(Variable), 778 ErrorInfo = {#varbind{oid = Oid, 779 variabletype = 'Counter32', 780 value = Val}, 781 SecName, 782 Opts}, 783 throw({error, Variable, ErrorInfo}). 784 785inc(Name) -> ets:update_counter(snmp_agent_table, Name, 1). 786 787get_counter(Name) -> 788 case (catch ets:lookup(snmp_agent_table, Name)) of 789 [{_, Val}] -> 790 Val; 791 _ -> 792 0 793 end. 794