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-module(snmp_user_based_sm_mib). 21 22%% Avoid warning for local function error/1 clashing with autoimported BIF. 23-compile({no_auto_import,[error/1]}). 24-export([configure/1, reconfigure/1, 25 usmUserSpinLock/1, usmUserSpinLock/2, 26 usmUserTable/1, usmUserTable/3, 27 table_next/2, 28 is_engine_id_known/1, get_user/2, get_user_from_security_name/2, 29 mk_key_change/3, mk_key_change/5, extract_new_key/3, mk_random/1]). 30-export([usmStatsUnsupportedSecLevels/1, 31 usmStatsNotInTimeWindows/1, 32 usmStatsUnknownUserNames/1, 33 usmStatsUnknownEngineIDs/1, 34 usmStatsWrongDigests/1, 35 usmStatsDecryptionErrors/1]). 36-export([add_user/1, add_user/13, delete_user/1]). 37 38%% Internal 39-export([check_usm/1]). 40 41-include("SNMP-USER-BASED-SM-MIB.hrl"). 42-include("SNMP-USM-AES-MIB.hrl"). 43-include("SNMPv2-TC.hrl"). 44-include("snmpa_internal.hrl"). 45-include("snmp_types.hrl"). 46 47-define(VMODULE,"USM_MIB"). 48-include("snmp_verbosity.hrl"). 49 50-ifndef(default_verbosity). 51-define(default_verbosity,silence). 52-endif. 53 54 55%% Columns not accessible via SNMP 56-define(usmUserAuthKey, 14). 57-define(usmUserPrivKey, 15). 58-define(is_cloning, 16). 59 60 61%%%----------------------------------------------------------------- 62%%% Utility functions 63%%%----------------------------------------------------------------- 64 65%%%----------------------------------------------------------------- 66%%% Implements the instrumentation functions and additional 67%%% functions for the SNMP-USER-BASED-SM-MIB. 68%%%----------------------------------------------------------------- 69 70%%----------------------------------------------------------------- 71%% Func: configure/1 72%% Args: Dir is the directory where the configuration files are found. 73%% Purpose: If the tables doesn't exist, this function reads 74%% the config-files for the USM tables, and 75%% inserts the data. This means that the data in the tables 76%% survive a reboot. However, the StorageType column is 77%% checked for each row. If volatile, the row is deleted. 78%% Returns: ok 79%% Fails: exit(configuration_error) 80%%----------------------------------------------------------------- 81configure(Dir) -> 82 set_sname(), 83 case db(usmUserTable) of 84 {_, mnesia} -> 85 ?vdebug("usm user table in mnesia: init vars & cleanup",[]), 86 init_vars(), 87 gc_tabs(); 88 TabDb -> 89 case snmpa_local_db:table_exists(TabDb) of 90 true -> 91 ?vdebug("usm user table exists: init vars & cleanup",[]), 92 init_vars(), 93 gc_tabs(); 94 false -> 95 ?vdebug("usm user table does not exist: reconfigure",[]), 96 reconfigure(Dir) 97 end 98 end. 99 100%%----------------------------------------------------------------- 101%% Func: reconfigure/1 102%% Args: Dir is the directory where the configuration files are found. 103%% Purpose: Reads the config-files for the USM tables, and 104%% inserts the data. Makes sure that all old data in 105%% the tables are deleted, and the new data inserted. 106%% This function makes sure that all (and only) 107%% config-file-data are in the tables. 108%% Returns: ok 109%% Fails: exit(configuration_error) | 110%% exit({unsupported_crypto, Function}) 111%%----------------------------------------------------------------- 112reconfigure(Dir) -> 113 set_sname(), 114 case (catch do_reconfigure(Dir)) of 115 ok -> 116 ok; 117 {error, Reason} -> 118 ?vinfo("reconfigure error: ~p", [Reason]), 119 config_err("reconfigure failed: ~p", [Reason]), 120 exit(configuration_error); 121 Error -> 122 ?vinfo("reconfigure failed: ~p", [Error]), 123 config_err("reconfigure failed: ~p", [Error]), 124 exit(configuration_error) 125 end. 126 127do_reconfigure(Dir) -> 128 ?vdebug("read usm configuration files",[]), 129 Users = read_usm_config_files(Dir), 130 ?vdebug("check users",[]), 131 check_users(Users), 132 ?vdebug("initiate tables",[]), 133 init_tabs(Users), 134 ?vdebug("initiate vars",[]), 135 init_vars(), 136 ok. 137 138 139read_usm_config_files(Dir) -> 140 ?vdebug("read usm config file",[]), 141 Gen = fun (D, Reason) -> generate_usm(D, Reason) end, 142 Order = fun snmp_conf:no_order/2, 143 Check = fun (Entry, State) -> {check_usm(Entry), State} end, 144 Filter = fun snmp_conf:no_filter/1, 145 [Usms] = 146 snmp_conf:read_files(Dir, [{"usm.conf", Gen, Order, Check, Filter}]), 147 Usms. 148 149 150generate_usm(Dir, _Reason) -> 151 info_msg("Incomplete configuration. Generating empty usm.conf.", []), 152 USMFile = filename:join(Dir, "usm.conf"), 153 ok = file:write_file(USMFile, list_to_binary([])), 154 []. 155 156 157check_usm({EngineID, Name, SecName, Clone, AuthP, AuthKeyC, OwnAuthKeyC, 158 PrivP, PrivKeyC, OwnPrivKeyC, Public, AuthKey, PrivKey}) -> 159 snmp_conf:check_string(EngineID), 160 snmp_conf:check_string(Name), 161 snmp_conf:check_string(SecName), 162 CloneVal = 163 case catch snmp_conf:check_atom(Clone,[{zeroDotZero,?zeroDotZero}]) of 164 {error, _} -> 165 snmp_conf:check_oid(Clone), 166 Clone; 167 {ok, X} -> 168 X 169 end, 170 AuthProtoAlt = [{usmNoAuthProtocol, ?usmNoAuthProtocol}, 171 {usmHMACSHAAuthProtocol, ?usmHMACSHAAuthProtocol}, 172 {usmHMACMD5AuthProtocol, ?usmHMACMD5AuthProtocol}], 173 {ok, AuthProto} = snmp_conf:check_atom(AuthP, AuthProtoAlt), 174 snmp_conf:check_string(AuthKeyC), 175 snmp_conf:check_string(OwnAuthKeyC), 176 PrivProtoAlt = [{usmNoPrivProtocol, ?usmNoPrivProtocol}, 177 {usmDESPrivProtocol, ?usmDESPrivProtocol}, 178 {usmAesCfb128Protocol, ?usmAesCfb128Protocol}], 179 {ok, PrivProto} = snmp_conf:check_atom(PrivP, PrivProtoAlt), 180 snmp_conf:check_string(PrivKeyC), 181 snmp_conf:check_string(OwnPrivKeyC), 182 snmp_conf:check_string(Public), 183 snmp_conf:check_string(AuthKey, alen(AuthP)), 184 snmp_conf:check_string(PrivKey, plen(PrivP)), 185 186 User = {EngineID, Name, SecName, 187 CloneVal, AuthProto, AuthKeyC, OwnAuthKeyC, 188 PrivProto, PrivKeyC, OwnPrivKeyC, Public, 189 ?'StorageType_nonVolatile', ?'RowStatus_active', AuthKey, PrivKey}, 190 {ok, User}; 191check_usm(X) -> 192 error({invalid_user, X}). 193 194alen(usmNoAuthProtocol) -> any; 195alen(usmHMACMD5AuthProtocol) -> 16; 196alen(usmHMACSHAAuthProtocol) -> 20. 197 198plen(usmNoPrivProtocol) -> any; 199plen(usmDESPrivProtocol) -> 16; 200plen(usmAesCfb128Protocol) -> 16. 201 202 203%%----------------------------------------------------------------- 204%% This function loops through all users, and check that the 205%% definition is constistent with the support for crypto on 206%% the system. Thus, it is not possible to define a user that 207%% uses authentication if 'crypto' is not started, or a user that 208%% uses DES if 'crypto' doesn't support DES. 209%%----------------------------------------------------------------- 210check_users([User | Users]) -> 211 check_user(User), 212 check_users(Users); 213check_users([]) -> 214 ok. 215 216check_user(User) -> 217 case element(?usmUserAuthProtocol, User) of 218 ?usmNoAuthProtocol -> ok; 219 ?usmHMACMD5AuthProtocol -> 220 case is_crypto_supported(md5) of 221 true -> ok; 222 false -> exit({unsupported_crypto, md5_mac_96}) 223 end; 224 ?usmHMACSHAAuthProtocol -> 225 case is_crypto_supported(sha) of 226 true -> ok; 227 false -> exit({unsupported_crypto, sha_mac_96}) 228 end 229 end, 230 case element(?usmUserPrivProtocol, User) of 231 ?usmNoPrivProtocol -> ok; 232 ?usmDESPrivProtocol -> 233 case is_crypto_supported(des_cbc) of 234 true -> ok; 235 false -> exit({unsupported_crypto, des_cbc}) 236 end; 237 ?usmAesCfb128Protocol -> 238 case is_crypto_supported(aes_cfb128) of 239 true -> ok; 240 false -> exit({unsupported_crypto, aes_cfb128}) 241 end 242 end. 243 244 245init_tabs(Users) -> 246 ?vdebug("create usm user table",[]), 247 snmpa_local_db:table_delete(db(usmUserTable)), 248 snmpa_local_db:table_create(db(usmUserTable)), 249 init_user_table(Users). 250 251init_user_table([Row | T]) -> 252 Key = mk_user_table_key(Row), 253 snmpa_local_db:table_create_row(db(usmUserTable), Key, Row), 254 init_user_table(T); 255init_user_table([]) -> true. 256 257mk_user_table_key(Row) -> 258 Key1 = element(1, Row), 259 Key2 = element(2, Row), 260 [length(Key1) | Key1] ++ [length(Key2) | Key2]. 261 262table_cre_row(Tab, Key, Row) -> 263 snmpa_mib_lib:table_cre_row(db(Tab), Key, Row). 264 265table_del_row(Tab, Key) -> 266 snmpa_mib_lib:table_del_row(db(Tab), Key). 267 268 269add_user(EngineID, Name, SecName, Clone, AuthP, AuthKeyC, OwnAuthKeyC, 270 PrivP, PrivKeyC, OwnPrivKeyC, Public, AuthKey, PrivKey) -> 271 User = {EngineID, Name, SecName, Clone, AuthP, AuthKeyC, OwnAuthKeyC, 272 PrivP, PrivKeyC, OwnPrivKeyC, Public, AuthKey, PrivKey}, 273 add_user(User). 274 275add_user(User) -> 276 case (catch check_usm(User)) of 277 {ok, Row} -> 278 case (catch check_user(Row)) of 279 {'EXIT', Reason} -> 280 {error, Reason}; 281 _ -> 282 Key = mk_user_table_key(Row), 283 case table_cre_row(usmUserTable, Key, Row) of 284 true -> 285 {ok, Key}; 286 false -> 287 {error, create_failed} 288 end 289 end; 290 {error, Reason} -> 291 {error, Reason}; 292 Error -> 293 {error, Error} 294 end. 295 296delete_user(Key) -> 297 case table_del_row(usmUserTable, Key) of 298 true -> 299 ok; 300 false -> 301 {error, delete_failed} 302 end. 303 304 305gc_tabs() -> 306 DB = db(usmUserTable), 307 STC = stc(usmUserTable), 308 FOI = foi(usmUserTable), 309 snmpa_mib_lib:gc_tab(DB, STC, FOI), 310 ok. 311 312 313%%----------------------------------------------------------------- 314%% Counter functions 315%%----------------------------------------------------------------- 316 317usmStatsUnsupportedSecLevels(print) -> 318 VarAndValue = [{usmStatsUnsupportedSecLevels, 319 usmStatsUnsupportedSecLevels(get)}], 320 snmpa_mib_lib:print_variables(VarAndValue); 321usmStatsUnsupportedSecLevels(get) -> 322 get_counter(usmStatsUnsupportedSecLevels). 323 324usmStatsNotInTimeWindows(print) -> 325 VarAndValue = [{usmStatsNotInTimeWindows, usmStatsNotInTimeWindows(get)}], 326 snmpa_mib_lib:print_variables(VarAndValue); 327usmStatsNotInTimeWindows(get) -> 328 get_counter(usmStatsNotInTimeWindows). 329 330usmStatsUnknownUserNames(print) -> 331 VarAndValue = [{usmStatsUnknownUserNames, usmStatsUnknownUserNames(get)}], 332 snmpa_mib_lib:print_variables(VarAndValue); 333usmStatsUnknownUserNames(get) -> 334 get_counter(usmStatsUnknownUserNames). 335 336usmStatsUnknownEngineIDs(print) -> 337 VarAndValue = [{usmStatsUnknownEngineIDs, usmStatsUnknownEngineIDs(get)}], 338 snmpa_mib_lib:print_variables(VarAndValue); 339usmStatsUnknownEngineIDs(get) -> 340 get_counter(usmStatsUnknownEngineIDs). 341 342usmStatsWrongDigests(print) -> 343 VarAndValue = [{usmStatsWrongDigests, usmStatsWrongDigests(get)}], 344 snmpa_mib_lib:print_variables(VarAndValue); 345usmStatsWrongDigests(get) -> 346 get_counter(usmStatsWrongDigests). 347 348usmStatsDecryptionErrors(print) -> 349 VarAndValue = [{usmStatsDecryptionErrors, usmStatsDecryptionErrors(get)}], 350 snmpa_mib_lib:print_variables(VarAndValue); 351usmStatsDecryptionErrors(get) -> 352 get_counter(usmStatsDecryptionErrors). 353 354 355get_counter(Name) -> 356 case (catch ets:lookup(snmp_agent_table, Name)) of 357 [{_, Val}] -> 358 {value, Val}; 359 _ -> 360 genErr 361 end. 362 363 364init_vars() -> lists:map(fun maybe_create_var/1, vars()). 365 366maybe_create_var(Var) -> 367 case ets:lookup(snmp_agent_table, Var) of 368 [_] -> ok; 369 _ -> init_var(Var) 370 end. 371 372init_var(Var) -> ets:insert(snmp_agent_table, {Var, 0}). 373 374vars() -> 375 [ 376 usmStatsUnsupportedSecLevels, 377 usmStatsNotInTimeWindows, 378 usmStatsUnknownUserNames, 379 usmStatsUnknownEngineIDs, 380 usmStatsWrongDigests, 381 usmStatsDecryptionErrors 382 ]. 383 384 385%%----------------------------------------------------------------- 386%% API functions 387%%----------------------------------------------------------------- 388is_engine_id_known(EngineID) -> 389 EngineKey = [length(EngineID) | EngineID], 390 ?vtrace("is_engine_id_known -> EngineKey: ~w", [EngineKey]), 391 case table_next(usmUserTable, EngineKey) of 392 endOfTable -> false; 393 Key -> 394 ?vtrace("is_engine_id_known -> Key: ~w", [Key]), 395 lists:prefix(EngineKey, Key) 396 end. 397 398get_user(EngineID, UserName) -> 399 Key = [length(EngineID) | EngineID] ++ [length(UserName) | UserName], 400 snmp_generic:table_get_row(db(usmUserTable), Key, foi(usmUserTable)). 401 402get_user_from_security_name(EngineID, SecName) -> 403 %% Since the normal mapping between UserName and SecName is the 404 %% identityfunction, we first try to use the SecName as UserName, 405 %% and check the resulting row. If it doesn't match, we'll have to 406 %% loop through the entire table. 407 Key = [length(EngineID) | EngineID] ++ [length(SecName) | SecName], 408 case snmp_generic:table_get_row(db(usmUserTable), Key, 409 foi(usmUserTable)) of 410 Row when is_tuple(Row) -> 411 ?vtrace("get_user_from_security_name -> " 412 "found user using the identity function", []), 413 Row; 414 undefined -> 415 ?vtrace("get_user_from_security_name -> " 416 "user *not* found using the identity function", []), 417 F = fun(_, Row) when ( 418 (element(?usmUserEngineID,Row) =:= EngineID) andalso 419 (element(?usmUserSecurityName,Row) =:= SecName)) -> 420 throw({ok, Row}); 421 (_, _) -> 422 ok 423 end, 424 case catch snmp_generic:table_foreach(db(usmUserTable), F) of 425 {ok, Row} -> 426 Row; 427 _Else -> 428 undefined 429 end 430 end. 431 432 433%%----------------------------------------------------------------- 434%% Instrumentation Functions 435%%----------------------------------------------------------------- 436 437usmUserSpinLock(print) -> 438 VarAndValue = [{usmUserSpinLock, usmUserSpinLock(get)}], 439 snmpa_mib_lib:print_variables(VarAndValue); 440 441usmUserSpinLock(new) -> 442 snmp_generic:variable_func(new, {usmUserSpinLock, volatile}), 443 ?SNMP_RAND_SEED(), 444 %% rand:seed(exrop, 445 %% {erlang:phash2([node()]), 446 %% erlang:monotonic_time(), 447 %% erlang:unique_integer()}), 448 Val = rand:uniform(2147483648) - 1, 449 snmp_generic:variable_func(set, Val, {usmUserSpinLock, volatile}); 450 451usmUserSpinLock(delete) -> 452 ok; 453 454usmUserSpinLock(get) -> 455 snmp_generic:variable_func(get, {usmUserSpinLock, volatile}). 456 457usmUserSpinLock(is_set_ok, NewVal) -> 458 case snmp_generic:variable_func(get, {usmUserSpinLock, volatile}) of 459 {value, NewVal} -> noError; 460 _ -> inconsistentValue 461 end; 462usmUserSpinLock(set, NewVal) -> 463 snmp_generic:variable_func(set, (NewVal + 1) rem 2147483648, 464 {usmUserSpinLock, volatile}). 465 466 467%% Op = print - Used for debugging purposes 468usmUserTable(print) -> 469 Table = usmUserTable, 470 DB = db(Table), 471 FOI = foi(Table), 472 %% TableInfo = snmpa_mib_lib:get_table(db(Table), foi(Table)), 473 PrintRow = 474 fun(Prefix, Row) -> 475 lists:flatten( 476 io_lib:format("~sEngineID: ~p" 477 "~n~sName: ~p" 478 "~n~sSecurityName: ~p" 479 "~n~sCloneFrom: ~p" 480 "~n~sAuthProtocol: ~p (~w)" 481 "~n~sAuthKeyChange: ~p" 482 "~n~sOwnAuthKeyChange: ~p" 483 "~n~sPrivProtocol: ~p (~w)" 484 "~n~sPrivKeyChange: ~p" 485 "~n~sOwnPrivKeyChange: ~p" 486 "~n~sPublic: ~p" 487 "~n~sStorageType: ~p (~w)" 488 "~n~sStatus: ~p (~w)", 489 [Prefix, element(?usmUserEngineID, Row), 490 Prefix, element(?usmUserName, Row), 491 Prefix, element(?usmUserSecurityName, Row), 492 Prefix, element(?usmUserCloneFrom, Row), 493 Prefix, element(?usmUserAuthProtocol, Row), 494 case element(?usmUserAuthProtocol, Row) of 495 ?usmNoAuthProtocol -> none; 496 ?usmHMACMD5AuthProtocol -> md5; 497 ?usmHMACSHAAuthProtocol -> sha; 498 md5 -> md5; 499 sha -> sha; 500 _ -> undefined 501 end, 502 Prefix, element(?usmUserAuthKeyChange, Row), 503 Prefix, element(?usmUserOwnAuthKeyChange, Row), 504 Prefix, element(?usmUserPrivProtocol, Row), 505 case element(?usmUserPrivProtocol, Row) of 506 ?usmNoPrivProtocol -> none; 507 ?usmDESPrivProtocol -> des; 508 ?usmAesCfb128Protocol -> aes; 509 des -> des; 510 aes -> aes; 511 _ -> undefined 512 end, 513 Prefix, element(?usmUserPrivKeyChange, Row), 514 Prefix, element(?usmUserOwnPrivKeyChange, Row), 515 Prefix, element(?usmUserPublic, Row), 516 Prefix, element(?usmUserStorageType, Row), 517 case element(?usmUserStorageType, Row) of 518 ?'usmUserStorageType_readOnly' -> readOnly; 519 ?'usmUserStorageType_permanent' -> permanent; 520 ?'usmUserStorageType_nonVolatile' -> nonVolatile; 521 ?'usmUserStorageType_volatile' -> volatile; 522 ?'usmUserStorageType_other' -> other; 523 _ -> undefined 524 end, 525 Prefix, element(?usmUserStatus, Row), 526 case element(?usmUserStatus, Row) of 527 ?'usmUserStatus_destroy' -> destroy; 528 ?'usmUserStatus_createAndWait' -> createAndWait; 529 ?'usmUserStatus_createAndGo' -> createAndGo; 530 ?'usmUserStatus_notReady' -> notReady; 531 ?'usmUserStatus_notInService' -> notInService; 532 ?'usmUserStatus_active' -> active; 533 _ -> undefined 534 end])) 535 end, 536 snmpa_mib_lib:print_table(Table, DB, FOI, PrintRow); 537%% Op == new | delete 538usmUserTable(Op) -> 539 snmp_generic:table_func(Op, db(usmUserTable)). 540 541%% Op == get | is_set_ok | set | get_next 542usmUserTable(get, RowIndex, Cols) -> 543 get_patch(Cols, get(usmUserTable, RowIndex, Cols)); 544usmUserTable(get_next, RowIndex, Cols) -> 545 next_patch(next(usmUserTable, RowIndex, Cols)); 546usmUserTable(is_set_ok, RowIndex, Cols0) -> 547 ?vtrace("usmUserTable(is_set_ok) -> entry with" 548 "~n RowIndex: ~p" 549 "~n Cols0: ~p", [RowIndex, Cols0]), 550 case (catch verify_usmUserTable_cols(Cols0, [])) of 551 {ok, Cols} -> 552 ?vtrace("usmUserTable(is_set_ok) -> verified: " 553 "~n Cols: ~p", [Cols]), 554 %% Add a dummy value for securityName; otherwise snmp_generic will 555 %% think that a value is missing, so the row can't be created. 556 %% Note: this value is only added for is_set_ok, not for set! 557 NCols = [{?usmUserSecurityName, ""} | Cols], 558 IsSetOkRes = snmp_generic:table_func(is_set_ok, RowIndex, 559 NCols, db(usmUserTable)), 560 ?vtrace("usmUserTable(is_set_ok) -> tested: " 561 "~n IsSetOkRes: ~p", [IsSetOkRes]), 562 validate_is_set_ok(IsSetOkRes, RowIndex, Cols); 563 Error -> 564 Error 565 end; 566usmUserTable(set, RowIndex, Cols0) -> 567 ?vtrace("usmUserTable(set) -> entry with" 568 "~n RowIndex: ~p" 569 "~n Cols0: ~p", [RowIndex, Cols0]), 570 case (catch verify_usmUserTable_cols(Cols0, [])) of 571 {ok, Cols} -> 572 ?vtrace("usmUserTable(set) -> verified" 573 "~n Cols: ~p", [Cols]), 574 % check whether we're cloning. if so, get cloned params and add a few 575 % defaults that might be needed. 576 NCols = pre_set(RowIndex, validate_clone_from(RowIndex, Cols)), 577 ?vtrace("usmUserTable(set) -> pre-set: " 578 "~n NCols: ~p", [NCols]), 579 %% NOTE: The NCols parameter is sent to snmp_generic, but not to 580 %% validate_set! The reason is that the columns from pre_set are 581 %% set in snmp_generic, but not used by validate_set. 582 validate_set(snmp_generic:table_func(set, RowIndex, 583 NCols, db(usmUserTable)), 584 RowIndex, Cols); 585 Error -> 586 Error 587 end; 588usmUserTable(Op, Arg1, Arg2) -> 589 snmp_generic:table_func(Op, Arg1, Arg2, db(usmUserTable)). 590 591 592verify_usmUserTable_cols([], Cols) -> 593 ?vtrace("verify_usmUserTable_cols -> entry when done with" 594 "~n Cols: ~p", [Cols]), 595 {ok, lists:reverse(Cols)}; 596verify_usmUserTable_cols([{Col, Val0}|Cols], Acc) -> 597 ?vtrace("verify_usmUserTable_cols -> entry with" 598 "~n Col: ~p" 599 "~n Val0: ~p", [Col, Val0]), 600 Val = verify_usmUserTable_col(Col, Val0), 601 ?vtrace("verify_usmUserTable_cols -> verified: " 602 "~n Val: ~p", [Val]), 603 verify_usmUserTable_cols(Cols, [{Col, Val}|Acc]). 604 605verify_usmUserTable_col(?usmUserEngineID, EngineID) -> 606 case (catch snmp_conf:check_string(EngineID)) of 607 ok -> 608 EngineID; 609 _ -> 610 wrongValue(?usmUserEngineID) 611 end; 612verify_usmUserTable_col(?usmUserName, Name) -> 613 case (catch snmp_conf:check_string(Name)) of 614 ok -> 615 Name; 616 _ -> 617 wrongValue(?usmUserName) 618 end; 619verify_usmUserTable_col(?usmUserSecurityName, Name) -> 620 case (catch snmp_conf:check_string(Name)) of 621 ok -> 622 Name; 623 _ -> 624 wrongValue(?usmUserSecurityName) 625 end; 626verify_usmUserTable_col(?usmUserCloneFrom, Clone) -> 627 case Clone of 628 zeroDotZero -> ?zeroDotZero; 629 ?zeroDotZero -> ?zeroDotZero; 630 _ -> 631 case (catch snmp_conf:check_oid(Clone)) of 632 ok -> 633 Clone; 634 _ -> 635 wrongValue(?usmUserCloneFrom) 636 end 637 end; 638verify_usmUserTable_col(?usmUserAuthProtocol, AuthP) -> 639 case AuthP of 640 usmNoAuthProtocol -> ?usmNoAuthProtocol; 641 usmHMACSHAAuthProtocol -> ?usmHMACSHAAuthProtocol; 642 usmHMACMD5AuthProtocol -> ?usmHMACMD5AuthProtocol; 643 ?usmNoAuthProtocol -> ?usmNoAuthProtocol; 644 ?usmHMACSHAAuthProtocol -> ?usmHMACSHAAuthProtocol; 645 ?usmHMACMD5AuthProtocol -> ?usmHMACMD5AuthProtocol; 646 _ -> 647 wrongValue(?usmUserAuthProtocol) 648 end; 649verify_usmUserTable_col(?usmUserAuthKeyChange, AKC) -> 650 case (catch snmp_conf:check_string(AKC)) of 651 ok -> 652 AKC; 653 _ -> 654 wrongValue(?usmUserAuthKeyChange) 655 end; 656verify_usmUserTable_col(?usmUserOwnAuthKeyChange, OAKC) -> 657 case (catch snmp_conf:check_string(OAKC)) of 658 ok -> 659 OAKC; 660 _ -> 661 wrongValue(?usmUserOwnAuthKeyChange) 662 end; 663verify_usmUserTable_col(?usmUserPrivProtocol, PrivP) -> 664 case PrivP of 665 usmNoPrivProtocol -> ?usmNoPrivProtocol; 666 usmDESPrivProtocol -> ?usmDESPrivProtocol; 667 usmAesCfb128Protocol -> ?usmAesCfb128Protocol; 668 ?usmNoPrivProtocol -> ?usmNoPrivProtocol; 669 ?usmDESPrivProtocol -> ?usmDESPrivProtocol; 670 ?usmAesCfb128Protocol -> ?usmAesCfb128Protocol; 671 _ -> 672 wrongValue(?usmUserPrivProtocol) 673 end; 674verify_usmUserTable_col(?usmUserPrivKeyChange, PKC) -> 675 case (catch snmp_conf:check_string(PKC)) of 676 ok -> 677 PKC; 678 _ -> 679 wrongValue(?usmUserPrivKeyChange) 680 end; 681verify_usmUserTable_col(?usmUserOwnPrivKeyChange, OPKC) -> 682 case (catch snmp_conf:check_string(OPKC)) of 683 ok -> 684 OPKC; 685 _ -> 686 wrongValue(?usmUserOwnPrivKeyChange) 687 end; 688verify_usmUserTable_col(?usmUserPublic, Public) -> 689 case (catch snmp_conf:check_string(Public)) of 690 ok -> 691 Public; 692 _ -> 693 wrongValue(?usmUserPublic) 694 end; 695verify_usmUserTable_col(_, Val) -> 696 Val. 697 698 699%% Patch the values stored in the DB with other values for some 700%% objects. 701get_patch([?usmUserCloneFrom | Cols], [{value, _Val} | Vals]) -> 702 [{value, ?zeroDotZero} | get_patch(Cols, Vals)]; 703get_patch([?usmUserAuthKeyChange | Cols], [{value, _Val} | Vals]) -> 704 [{value, ""} | get_patch(Cols, Vals)]; 705get_patch([?usmUserOwnAuthKeyChange | Cols], [{value, _Val} | Vals]) -> 706 [{value, ""} | get_patch(Cols, Vals)]; 707get_patch([?usmUserPrivKeyChange | Cols], [{value, _Val} | Vals]) -> 708 [{value, ""} | get_patch(Cols, Vals)]; 709get_patch([?usmUserOwnPrivKeyChange | Cols], [{value, _Val} | Vals]) -> 710 [{value, ""} | get_patch(Cols, Vals)]; 711get_patch([_Col | Cols], [Val | Vals]) -> 712 [Val | get_patch(Cols, Vals)]; 713get_patch(_Cols, Result) -> 714 Result. 715 716next_patch([{[?usmUserCloneFrom | Idx], _Val} | Vals]) -> 717 [{[?usmUserCloneFrom | Idx], ?zeroDotZero} | next_patch(Vals)]; 718next_patch([{[?usmUserAuthKeyChange | Idx], _Val} | Vals]) -> 719 [{[?usmUserAuthKeyChange | Idx], ""} | next_patch(Vals)]; 720next_patch([{[?usmUserOwnAuthKeyChange | Idx], _Val} | Vals]) -> 721 [{[?usmUserOwnAuthKeyChange | Idx], ""} | next_patch(Vals)]; 722next_patch([{[?usmUserPrivKeyChange | Idx], _Val} | Vals]) -> 723 [{[?usmUserPrivKeyChange | Idx], ""} | next_patch(Vals)]; 724next_patch([{[?usmUserOwnPrivKeyChange | Idx], _Val} | Vals]) -> 725 [{[?usmUserOwnPrivKeyChange | Idx], ""} | next_patch(Vals)]; 726next_patch([Val | Vals]) -> 727 [Val | next_patch(Vals)]; 728next_patch(Result) -> Result. 729 730 731validate_is_set_ok({noError, 0}, RowIndex, Cols) -> 732 case (catch do_validate_is_set_ok(RowIndex, Cols)) of 733 ok -> 734 {noError, 0}; 735 Error -> 736 Error 737 end; 738validate_is_set_ok(Error, _RowIndex, _Cols) -> 739 Error. 740 741do_validate_is_set_ok(RowIndex, Cols) -> 742 NCols = validate_clone_from(RowIndex, Cols), 743 validate_auth_protocol(RowIndex, NCols), 744 validate_auth_key_change(RowIndex, NCols), 745 validate_own_auth_key_change(RowIndex, NCols), 746 validate_priv_protocol(RowIndex, NCols), 747 validate_priv_key_change(RowIndex, NCols), 748 validate_own_priv_key_change(RowIndex, NCols), 749 ok. 750 751pre_set(RowIndex, Cols) -> 752 %% Remove the ?is_cloning member again; it must no longer be 753 %% present. 754 Cols0 = key1delete(?is_cloning, Cols), 755 %% Possibly initialize the usmUserSecurityName and privacy keys 756 case snmp_generic:table_row_exists(db(usmUserTable), RowIndex) of 757 true -> Cols0; 758 false -> 759 SecName = get_user_name(RowIndex), 760 Cols1 = [{?usmUserSecurityName, SecName} | Cols0], 761 case proplists:get_value(?is_cloning, Cols) of 762 true -> 763 % the row is just being cloned. the cloned user's 764 % passwords are already present in Cols and must 765 % not be overwritten. 766 Cols1; 767 _ -> 768 Cols1 ++ [{?usmUserAuthKey, ""}, 769 {?usmUserPrivKey, ""}] 770 end 771 end. 772 773validate_set({noError, 0}, RowIndex, Cols) -> 774 %% Now, all is_set_ok validation steps have been executed. So 775 %% everything is ready for the set. 776 set_auth_key_change(RowIndex, Cols), 777 set_own_auth_key_change(RowIndex, Cols), 778 set_priv_key_change(RowIndex, Cols), 779 set_own_priv_key_change(RowIndex, Cols), 780 {noError, 0}; 781validate_set(Error, _RowIndex, _Cols) -> 782 Error. 783 784%%----------------------------------------------------------------- 785%% Here's the alg: If this is the first time the CloneFrom is written, 786%% we must check that the CloneFrom row exists, so we can invoke the 787%% clone process in the set phase. Otherwise, the set succed, with 788%% no further checks. 789%%----------------------------------------------------------------- 790validate_clone_from(RowIndex, Cols) -> 791 case key1search(?usmUserCloneFrom, Cols) of 792 {value, {_Col, RowPointer}} -> 793 RowIndex2 = extract_row(RowPointer), 794 OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable), 795 RowIndex, 796 ?usmUserCloneFrom), 797 case OldCloneFrom of 798 {value, Val} when Val /= noinit -> 799 %% This means that the cloning is already done... 800 no_cloning(Cols); 801 _ -> 802 %% Otherwise, we must check the CloneFrom value. It 803 %% must relate to a usmUserEntry that exists and is active. 804 case snmp_generic:table_get_row(db(usmUserTable), RowIndex2) of 805 CloneFromRow when is_tuple(CloneFromRow) -> 806 case element(?usmUserStatus, CloneFromRow) of 807 ?'RowStatus_active' -> 808 get_cloned_cols(CloneFromRow, Cols); 809 _ -> 810 inconsistentName(?usmUserCloneFrom) 811 end; 812 undefined -> 813 inconsistentName(?usmUserCloneFrom) 814 end 815 end; 816 false -> 817 % no ?usmUserCloneFrom specified, don't modify columns 818 no_cloning(Cols) 819 end. 820 821get_cloned_cols(CloneFromRow, Cols) -> 822 % initialize cloned columns with data from CloneFromRow 823 % and overwrite that again with data found in Cols 824 AuthP = element(?usmUserAuthProtocol, CloneFromRow), 825 PrivP = element(?usmUserPrivProtocol, CloneFromRow), 826 AuthK = element(?usmUserAuthKey, CloneFromRow), 827 PrivK = element(?usmUserPrivKey, CloneFromRow), 828 ClonedCols = [{?usmUserAuthProtocol, AuthP}, 829 {?usmUserPrivProtocol, PrivP}, 830 {?usmUserAuthKey, AuthK}, 831 {?usmUserPrivKey, PrivK}, 832 {?is_cloning, true} 833 ], 834 Func = fun({Col, _} = Item, NCols) -> 835 key1store(Col, NCols, Item) 836 end, 837 Cols1 = lists:foldl(Func, ClonedCols, Cols), 838 key1sort(Cols1). 839 840no_cloning(Cols0) -> 841 Cols1 = key1delete(?usmUserCloneFrom, Cols0), 842 key1delete(?is_cloning, Cols1). 843 844 845validate_auth_protocol(RowIndex, Cols) -> 846 case key1search(?usmUserAuthProtocol, Cols) of 847 {value, {_Col, AuthProtocol}} -> 848 %% Check if the row is being cloned; we can't check the 849 %% old value of authProtocol, because if the row was 850 %% createAndWaited, the default value would have been 851 %% written (usmNoAuthProtocol). 852 IsCloning = proplists:get_value(?is_cloning, Cols, false), 853 if 854 not IsCloning -> 855 %% This means that the row is not being cloned right 856 %% now; set is ok if new protocol is usmNoAuthProtocol 857 case AuthProtocol of 858 ?usmNoAuthProtocol -> 859 %% Check that the Priv protocl is noPriv 860 case get_priv_proto(RowIndex, Cols) of 861 ?usmNoPrivProtocol -> ok; 862 _ -> inconsistentValue(?usmUserAuthProtocol) 863 end; 864 ?usmHMACMD5AuthProtocol -> 865 inconsistentValue(?usmUserAuthProtocol); 866 ?usmHMACSHAAuthProtocol -> 867 inconsistentValue(?usmUserAuthProtocol); 868 _ -> 869 wrongValue(?usmUserAuthProtocol) 870 end; 871 true -> 872 %% Otherwise, check that the new protocol is known, 873 %% and that the system we're running supports the 874 %% hash function. 875 case AuthProtocol of 876 ?usmNoAuthProtocol -> 877 %% Check that the Priv protocl is noPriv 878 case get_priv_proto(RowIndex, Cols) of 879 ?usmNoPrivProtocol -> ok; 880 _ -> inconsistentValue(?usmUserAuthProtocol) 881 end; 882 ?usmHMACMD5AuthProtocol -> 883 case is_crypto_supported(md5) of 884 true -> ok; 885 false -> 886 wrongValue(?usmUserAuthProtocol) 887 end; 888 ?usmHMACSHAAuthProtocol -> 889 case is_crypto_supported(sha) of 890 true -> ok; 891 false -> 892 wrongValue(?usmUserAuthProtocol) 893 end; 894 _ -> wrongValue(?usmUserAuthProtocol) 895 end 896 end; 897 false -> 898 ok 899 end. 900 901validate_auth_key_change(RowIndex, Cols) -> 902 validate_key_change(RowIndex, Cols, ?usmUserAuthKeyChange, auth). 903 904validate_own_auth_key_change(RowIndex, Cols) -> 905 validate_requester(RowIndex, Cols, ?usmUserOwnAuthKeyChange), 906 validate_key_change(RowIndex, Cols, ?usmUserOwnAuthKeyChange, auth). 907 908validate_priv_key_change(RowIndex, Cols) -> 909 validate_key_change(RowIndex, Cols, ?usmUserPrivKeyChange, priv). 910 911validate_own_priv_key_change(RowIndex, Cols) -> 912 validate_requester(RowIndex, Cols, ?usmUserOwnPrivKeyChange), 913 validate_key_change(RowIndex, Cols, ?usmUserOwnPrivKeyChange, priv). 914 915%% Check that the requesting user is the same as the modified user 916validate_requester(RowIndex, Cols, KeyChangeCol) -> 917 case key1search(KeyChangeCol, Cols) of 918 {value, _} -> 919 case get(sec_model) of % Check the securityModel in the request 920 ?SEC_USM -> ok; 921 _ -> noAccess(KeyChangeCol) 922 end, 923 %% The SecurityName may not be set yet. First, check if it is set. 924 SecNameForUser = 925 case snmp_generic:table_get_element(db(usmUserTable), 926 RowIndex, 927 ?usmUserSecurityName) of 928 {value, Val} when Val /= noinit -> Val; 929 _ -> get_user_name(RowIndex) 930 end, 931 case get(sec_name) of % Check the securityName in the request 932 SecNameForUser -> ok; 933 _ -> noAccess(KeyChangeCol) 934 end; 935 false -> 936 ok 937 end. 938 939validate_key_change(RowIndex, Cols, KeyChangeCol, Type) -> 940 case key1search(KeyChangeCol, Cols) of 941 {value, {_Col, KeyC}} -> 942 %% Check if the row has been cloned; or if it is cloned in 943 %% this set-operation. 944 OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable), 945 RowIndex, 946 ?usmUserCloneFrom), 947 IsClonePresent = proplists:get_value(?is_cloning, Cols, false), 948 %% Set is ok if 1) the user already is created, 2) this is 949 %% a new user, which has been cloned, or is about to be 950 %% cloned. 951 case {OldCloneFrom, IsClonePresent} of 952 {{value, Val}, _} when Val /= noinit -> 953 %% The user exists, or has been cloned 954 ok; 955 {_, true} -> 956 %% The user is cloned in this operation 957 ok; 958 _ -> 959 %% The user doesn't exist, or hasn't been cloned, 960 %% and is not cloned in this operation. 961 inconsistentName(KeyChangeCol) 962 end, 963 %% Check that the length makes sense 964 Len = length(KeyC), 965 case Type of 966 auth -> 967 case get_auth_proto(RowIndex, Cols) of 968 ?usmNoAuthProtocol -> ok; 969 ?usmHMACMD5AuthProtocol when Len =:= 32 -> ok; 970 ?usmHMACSHAAuthProtocol when Len =:= 40 -> ok; 971 _ -> wrongValue(KeyChangeCol) 972 end; 973 priv -> 974 case get_priv_proto(RowIndex, Cols) of 975 ?usmNoPrivProtocol -> ok; 976 ?usmDESPrivProtocol when Len == 32 -> ok; 977 ?usmAesCfb128Protocol when Len == 32 -> ok; 978 _ -> wrongValue(KeyChangeCol) 979 end 980 end; 981 false -> 982 ok 983 end. 984 985validate_priv_protocol(RowIndex, Cols) -> 986 case key1search(?usmUserPrivProtocol, Cols) of 987 {value, {_Col, PrivProtocol}} -> 988 %% Check if the row has been cloned; we can't check the 989 %% old value of privhProtocol, because if the row was 990 %% createAndWaited, the default value would have been 991 %% written (usmNoPrivProtocol). 992 IsCloning = proplists:get_value(?is_cloning, Cols, false), 993 if 994 not IsCloning -> 995 %% This means that the cloning is already done; set is ok 996 %% if new protocol is usmNoPrivProtocol 997 case PrivProtocol of 998 ?usmNoPrivProtocol -> 999 ok; 1000 ?usmDESPrivProtocol -> 1001 inconsistentValue(?usmUserPrivProtocol); 1002 ?usmAesCfb128Protocol -> 1003 inconsistentValue(?usmUserPrivProtocol); 1004 _ -> 1005 wrongValue(?usmUserPrivProtocol) 1006 end; 1007 true -> 1008 %% Otherwise, check that the new protocol is known, 1009 %% and that the system we're running supports the 1010 %% crypto function. 1011 case PrivProtocol of 1012 ?usmNoPrivProtocol -> 1013 ok; 1014 ?usmDESPrivProtocol -> 1015 %% The 'catch' handles the case when 'crypto' is 1016 %% not present in the system. 1017 case is_crypto_supported(des_cbc) of 1018 true -> 1019 case get_auth_proto(RowIndex, Cols) of 1020 ?usmNoAuthProtocol -> 1021 inconsistentValue(?usmUserPrivProtocol); 1022 _ -> 1023 ok 1024 end; 1025 false -> 1026 wrongValue(?usmUserPrivProtocol) 1027 end; 1028 ?usmAesCfb128Protocol -> 1029 %% The 'catch' handles the case when 'crypto' is 1030 %% not present in the system. 1031 case is_crypto_supported(aes_cfb128) of 1032 true -> 1033 case get_auth_proto(RowIndex, Cols) of 1034 ?usmNoAuthProtocol -> 1035 inconsistentValue(?usmUserPrivProtocol); 1036 _ -> 1037 ok 1038 end; 1039 false -> 1040 wrongValue(?usmUserPrivProtocol) 1041 end; 1042 _ -> wrongValue(?usmUserPrivProtocol) 1043 end 1044 end; 1045 false -> 1046 ok 1047 end. 1048 1049 1050set_auth_key_change(RowIndex, Cols) -> 1051 set_key_change(RowIndex, Cols, ?usmUserAuthKeyChange, auth). 1052 1053set_own_auth_key_change(RowIndex, Cols) -> 1054 set_key_change(RowIndex, Cols, ?usmUserOwnAuthKeyChange, auth). 1055 1056set_priv_key_change(RowIndex, Cols) -> 1057 set_key_change(RowIndex, Cols, ?usmUserPrivKeyChange, priv). 1058 1059set_own_priv_key_change(RowIndex, Cols) -> 1060 set_key_change(RowIndex, Cols, ?usmUserOwnPrivKeyChange, priv). 1061 1062set_key_change(RowIndex, Cols, KeyChangeCol, Type) -> 1063 case key1search(KeyChangeCol, Cols) of 1064 {value, {_Col, KeyChange}} -> 1065 KeyCol = case Type of 1066 auth -> ?usmUserAuthKey; 1067 priv -> ?usmUserPrivKey 1068 end, 1069 [AuthP, Key] = 1070 snmp_generic:table_get_elements(db(usmUserTable), 1071 RowIndex, 1072 [?usmUserAuthProtocol, 1073 KeyCol]), 1074 NewKey = extract_new_key(AuthP, Key, KeyChange), 1075 snmp_generic:table_set_element(db(usmUserTable), RowIndex, 1076 KeyCol, NewKey); 1077 false -> 1078 ok 1079 end. 1080 1081%% Extract the UserName part from a RowIndex. 1082get_user_name([L1 | Rest]) -> get_user_name(L1, Rest). 1083get_user_name(0, [_L2 | UserName]) -> UserName; 1084get_user_name(N, [_H | T]) -> get_user_name(N-1, T). 1085 1086extract_row(RowPtr) -> extract_row(?usmUserEntry, RowPtr). 1087extract_row([H | T], [H | T2]) -> extract_row(T, T2); 1088extract_row([], [?usmUserSecurityName | T]) -> T; 1089extract_row(_, _) -> wrongValue(?usmUserCloneFrom). 1090 1091%% Pre: the user exists or is being cloned in this operation 1092get_auth_proto(RowIndex, Cols) -> 1093 %% The protocol can be changed by the request too, otherwise, 1094 %% check the stored protocol. 1095 case key1search(?usmUserAuthProtocol, Cols) of 1096 {value, {_, Protocol}} -> 1097 Protocol; 1098 false -> 1099 %% OTP-3596 1100 case snmp_generic:table_get_element(db(usmUserTable), 1101 RowIndex, 1102 ?usmUserAuthProtocol) of 1103 {value, Protocol} -> 1104 Protocol; 1105 _ -> 1106 undefined 1107 end 1108 end. 1109 1110%% Pre: the user exists or is being cloned in this operation 1111get_priv_proto(RowIndex, Cols) -> 1112 %% The protocol can be changed by the request too, otherwise, 1113 %% check the stored protocol. 1114 case key1search(?usmUserPrivProtocol, Cols) of 1115 {value, {_, Protocol}} -> 1116 Protocol; 1117 false -> 1118 %% OTP-3596 1119 case snmp_generic:table_get_element(db(usmUserTable), 1120 RowIndex, 1121 ?usmUserPrivProtocol) of 1122 {value, Protocol} -> 1123 Protocol; 1124 _ -> 1125 undefined 1126 end 1127 end. 1128 1129 1130db(X) -> snmpa_agent:db(X). 1131 1132fa(usmUserTable) -> ?usmUserSecurityName. 1133 1134foi(usmUserTable) -> ?usmUserEngineID. 1135 1136noc(usmUserTable) -> 13. 1137 1138stc(usmUserTable) -> ?usmUserStorageType. 1139 1140next(Name, RowIndex, Cols) -> 1141 snmp_generic:handle_table_next(db(Name), RowIndex, Cols, 1142 fa(Name), foi(Name), noc(Name)). 1143 1144table_next(Name, RestOid) -> 1145 snmp_generic:table_next(db(Name), RestOid). 1146 1147 1148get(Name, RowIndex, Cols) -> 1149 snmp_generic:handle_table_get(db(Name), RowIndex, Cols, foi(Name)). 1150 1151%%----------------------------------------------------------------- 1152%% Key change functions. The KeyChange Texual-Convention is 1153%% defined in the SNMP-USER-BASED-SM-MIB. 1154%% Note that this implementation supports md5 and sha, which 1155%% both have fixed length requirements on the length of the key; 1156%% thus the implementation can be (and is) simplified. 1157%%----------------------------------------------------------------- 1158mk_key_change(Hash, OldKey, NewKey) -> 1159 KeyLen = length(NewKey), 1160 Alg = case Hash of 1161 ?usmHMACMD5AuthProtocol -> md5; 1162 ?usmHMACSHAAuthProtocol -> sha; 1163 md5 -> md5; 1164 sha -> sha 1165 end, 1166 Random = mk_random(KeyLen), 1167 mk_key_change(Alg, OldKey, NewKey, KeyLen, Random). 1168 1169%% This function is only exported for test purposes. There is a test 1170%% case in the standard where Random is pre-defined. 1171mk_key_change(Alg, OldKey, NewKey, KeyLen, Random) -> 1172 %% OldKey and Random is of length KeyLen... 1173 Digest = lists:sublist(binary_to_list(crypto:hash(Alg, OldKey++Random)), KeyLen), 1174 %% ... and so is Digest 1175 Delta = snmp_misc:str_xor(Digest, NewKey), 1176 Random ++ Delta. 1177 1178%% Extracts a new Key from a KeyChange value, sent by a manager. 1179extract_new_key(?usmNoAuthProtocol, OldKey, _KeyChange) -> 1180 OldKey; 1181extract_new_key(Hash, OldKey, KeyChange) -> 1182 KeyLen = length(OldKey), 1183 Alg = case Hash of 1184 ?usmHMACMD5AuthProtocol -> md5; 1185 ?usmHMACSHAAuthProtocol -> sha; 1186 md5 -> md5; 1187 sha -> sha 1188 end, 1189 {Random, Delta} = split(KeyLen, KeyChange, []), 1190 Digest = lists:sublist(binary_to_list(crypto:hash(Alg, OldKey++Random)), KeyLen), 1191 NewKey = snmp_misc:str_xor(Digest, Delta), 1192 NewKey. 1193 1194-define(i16(Int), (Int bsr 8) band 255, Int band 255). 1195-define(i8(Int), Int band 255). 1196 1197mk_random(Len) when Len =< 20 -> 1198 binary_to_list(crypto:strong_rand_bytes(Len)). 1199 1200split(0, Rest, FirstRev) -> 1201 {lists:reverse(FirstRev), Rest}; 1202split(N, [H | T], FirstRev) when N > 0 -> 1203 split(N-1, T, [H | FirstRev]). 1204 1205 1206-compile({inline, [{is_crypto_supported,1}]}). 1207is_crypto_supported(Func) -> 1208 snmp_misc:is_crypto_supported(Func). 1209 1210 1211inconsistentValue(V) -> throw({inconsistentValue, V}). 1212inconsistentName(N) -> throw({inconsistentName, N}). 1213wrongValue(V) -> throw({wrongValue, V}). 1214noAccess(C) -> throw({noAccess, C}). 1215 1216set_sname() -> 1217 set_sname(get(sname)). 1218 1219set_sname(undefined) -> 1220 put(sname,conf); 1221set_sname(_) -> %% Keep it, if already set. 1222 ok. 1223 1224error(Reason) -> 1225 throw({error, Reason}). 1226 1227 1228%%----------------------------------------------------------------- 1229%% lists key-function(s) wrappers 1230 1231-compile({inline,key1delete/2}). 1232key1delete(Key, List) -> 1233 lists:keydelete(Key, 1, List). 1234 1235-compile({inline,key1search/2}). 1236key1search(Key, List) -> 1237 lists:keysearch(Key, 1, List). 1238 1239-compile({inline,key1store/3}). 1240key1store(Key, List, Elem) -> 1241 lists:keystore(Key, 1, List, Elem). 1242 1243-compile({inline,key1sort/1}). 1244key1sort(List) -> 1245 lists:keysort(1, List). 1246 1247 1248%%----------------------------------------------------------------- 1249 1250info_msg(F, A) -> 1251 ?snmpa_info("USM: " ++ F, A). 1252 1253%% --- 1254 1255config_err(F, A) -> 1256 snmpa_error:config_err("[USER-BASED-SM-MIB]: " ++ F, A). 1257 1258