From a31c7b2eb22d15f1ca6c2d6bca257faa2315fcf7 Mon Sep 17 00:00:00 2001 From: Stefan Zegenhagen Date: Mon, 10 Jun 2013 11:07:00 +0200 Subject: SNMP/VACM: bugfix for vacmViewTreeFamilyMask Dear all, it's almost a year since I sent the patch attached to this e-mail, and I just found out that I have not yet gotten a response to it. I would consider this patch important because it fixes an issue with the interpretation of data that might be critical for SNMPv3 operation. I confirmed at that time that erlangs interpretation of vacmViewTreeFamilyMask is indeed not interoperable with other SNMP stacks. Kind regards, > > > the implementation of SNMP-VIEW-BASED-ACM.mib assumes that the input for > > > vacmViewTreeFamilyMask is an OID consisting of 1's and 0's only to form > > > the mask. However, the MIB states that the input should be a bitstring. > > > > > > The OID representation of the mask is useful in the code as it speeds up > > > time-critical code paths when checking access permissions for EACH SNMP > > > access. Reading/writing the view mask objects is less time-critical. > > > > > > Therefore, to fix the issue, convert between OID representation and > > > bitstring when the vacmViewTreeFamilyMask objects are accessed. This is > > > done by the patch attached to this e-mail. > > > I'm very sorry for the troubles that I am causing but it seems that the > previous version of the patch did more than it should: the OID-bitstring > conversion was also applied to other tables in the same MIB on > get/get-next requests. > > The version of the patch that is attached to this e-mail restricts the > OID-bitstring conversion to vacmViewTreeFamilyMask alone. -- Dr. Stefan Zegenhagen arcutronix GmbH Garbsener Landstr. 10 30419 Hannover Germany Tel: +49 511 277-2734 Fax: +49 511 277-2709 Email: stefan.zegenhagen@arcutronix.com Web: www.arcutronix.com *Synchronize the Ethernet* General Managers: Dipl. Ing. Juergen Schroeder, Dr. Josef Gfrerer - Legal Form: GmbH, Registered office: Hannover, HRB 202442, Amtsgericht Hannover; Ust-Id: DE257551767. Please consider the environment before printing this message. >From aa2acfb8a0b5ae05fc5ba982d78ee5607384a2be Mon Sep 17 00:00:00 2001 From: Stefan Zegenhagen Date: Wed, 1 Aug 2012 09:56:15 +0200 Subject: [PATCH] bugfix for vacmViewTreeFamilyMask The vacmViewTreeFamilyMask is defined to be a bit string in the MIB, not an OID. However, the MIB implementation assumed the latter, effectively rendering all attempts to read/set masks via SNMP unsuccessful. Since the mask is used in hot paths (e.g. access permission checks for each SNMP operation, the OID representation of the mask has benefits (e.g. faster processing). Therefore, convert the bitstring to/from its OID representation when reading/setting any mask object. --- lib/snmp/src/agent/snmp_view_based_acm_mib.erl | 91 ++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 6 deletions(-) (limited to 'lib/snmp/src') diff --git a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl index ad9540e886..0acef780f8 100644 --- a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl +++ b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl @@ -957,9 +957,9 @@ verify_vacmViewTreeFamilyTable_col(?vacmViewTreeFamilyMask, Mask) -> null -> []; [] -> []; _ -> - case (catch snmp_conf:check_oid(Mask)) of + case (catch check_mask(Mask)) of ok -> - Mask; + mask2oid(Mask); _ -> wrongValue(?vacmViewTreeFamilyMask) end @@ -975,7 +975,54 @@ verify_vacmViewTreeFamilyTable_col(?vacmViewTreeFamilyType, Type) -> end; verify_vacmViewTreeFamilyTable_col(_, Val) -> Val. - + + +check_mask([]) -> + ok; +check_mask([Head | Tail]) when is_integer(Head) -> +% we can cause exception here because the caller catches + if + Head > -1 andalso Head < 256 -> + check_mask(Tail) + end. + +% internally (for easier computations), the mask is stored +% as OID and not as bit-field. Therefore, we have to convert +% the bitstring to a list of integers before storing it. +mask2oid(Mask) -> + Func = fun(Byte) -> + % what's a nice syntax for extracting bits + % from a byte??? + <> = <>, + [A, B, C, D, E, F, G, H] + end, + lists:flatten(lists:map(Func, Mask)). + +oid2mask(Oid) -> + % convert all suboids to either 1 or 0 for sure + Func = fun + (0) -> 0; + (_) -> 1 + end, + oid2mask(lists:map(Func, Oid), []). + +oid2mask([], Mask) -> + % end-of-list, return bitstring + lists:reverse(Mask); +oid2mask(Oid, Mask) -> + % check whether we've got at least 8 sub-identifiers. if not, + % extend with 1's until there are enough to fill a byte + NOid = case length(Oid) of + Small when Small < 8 -> + Oid ++ lists:duplicate(8-Small, 1); + _ -> + Oid + end, + % extract sufficient suboids for 8 bits + [A, B, C, D, E, F, G, H | Tail] = NOid, + <> = <>, + oid2mask(Tail, [Byte | Mask]). + table_next(Name, RestOid) -> snmp_generic:table_next(db(Name), RestOid). @@ -1014,11 +1061,43 @@ stc(vacmSecurityToGroupTable) -> ?vacmSecurityToGroupStorageType; stc(vacmViewTreeFamilyTable) -> ?vacmViewTreeFamilyStorageType. next(Name, RowIndex, Cols) -> - snmp_generic:handle_table_next(db(Name), RowIndex, Cols, - fa(Name), foi(Name), noc(Name)). + Ans = snmp_generic:handle_table_next(db(Name), RowIndex, Cols, + fa(Name), foi(Name), noc(Name)), + patch_next(Name, Ans). get(Name, RowIndex, Cols) -> - snmp_generic:handle_table_get(db(Name), RowIndex, Cols, foi(Name)). + Ans = snmp_generic:handle_table_get(db(Name), RowIndex, Cols, foi(Name)), + patch(Name, Cols, Ans). + + +patch(Name, Cols, Ans) when is_list(Ans) -> + % function to patch returned values + Func = fun + ({Col, {value, Val}}) -> {value, do_patch(Name, Col, Val)}; + ({_, Other}) -> Other + end, + % merge column numbers and return values. there must be as much + % return values as there are columns requested + Tmp = lists:zip(Cols, Ans), + % and patch all values + lists:map(Func, Tmp); +patch(_, _, Ans) -> + Ans. + +patch_next(Name, Ans) when is_list(Ans) -> + Func = fun + ({[C | _] = Idx, Val}) -> {Idx, do_patch(Name, C, Val)}; + (Other) -> Other + end, + lists:map(Func, Ans); +patch_next(_, Ans) -> + Ans. + +do_patch(vacmViewTreeFamilyTable, ?vacmViewTreeFamilyMask, Val) -> + oid2mask(Val); +do_patch(_, _, Val) -> + Val. + wrongValue(V) -> throw({wrongValue, V}). -- cgit v1.2.3 From 7147e7a36b8a22ba6345356aa786029bab66f4e6 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 3 Jul 2013 09:39:40 +0200 Subject: [snmp/agent] Local DB counter increment wrap error The counter increment function in the local-db was incorrect. It did not handle counter wrap correctly. OTP-11192 --- lib/snmp/src/agent/snmpa_local_db.erl | 2 +- lib/snmp/src/app/snmp.appup.src | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'lib/snmp/src') diff --git a/lib/snmp/src/agent/snmpa_local_db.erl b/lib/snmp/src/agent/snmpa_local_db.erl index 2c0cad807a..5198c6ec4e 100644 --- a/lib/snmp/src/agent/snmpa_local_db.erl +++ b/lib/snmp/src/agent/snmpa_local_db.erl @@ -583,7 +583,7 @@ handle_cast({variable_inc, Name, Db, N}, State) -> {value, Val} -> Val; _ -> 0 end, - insert(Db, Name, M+N rem 4294967296, State), + insert(Db, Name, (M+N) rem 4294967296, State), {noreply, State}; handle_cast({verbosity,Verbosity}, State) -> diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src index 7ffa4a725d..53de57efd3 100644 --- a/lib/snmp/src/app/snmp.appup.src +++ b/lib/snmp/src/app/snmp.appup.src @@ -17,18 +17,38 @@ %% %CopyrightEnd% %% + {"%VSN%", %% ----- U p g r a d e ------------------------------------------------------- +%% Instruction examples: +%% {restart_application, snmp} +%% {load_module, snmp_pdus, soft_purge, soft_purge, []} +%% {update, snmpa_local_db, soft, soft_purge, soft_purge, []} +%% {add_module, snmpm_net_if_mt} + [ + {"4.24", + [ + {update, snmpa_local_db, soft, soft_purge, soft_purge, []} + ] + }, {"4.23.1", [{restart_application, snmp}]}, {"4.23", [{restart_application, snmp}]} ], %% ------D o w n g r a d e --------------------------------------------------- +%% Instruction examples: +%% {remove, {snmpm_net_if_mt, soft_purge, soft_purge}} + [ + {"4.24", + [ + {update, snmpa_local_db, soft, soft_purge, soft_purge, []} + ] + }, {"4.23.1", [{restart_application, snmp}]}, {"4.23", [{restart_application, snmp}]} ] -- cgit v1.2.3 From a6ba7a3327b146d8472b154cc8ba4544f9d4d0fe Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 25 Jun 2013 12:38:17 +0200 Subject: [snmp/agent] Cleanup, renaming, appup, proper version and release notes Add utility functions for checking view masks. Code cleanup, function renaming and comment fix (%% instead of %). Also updated the mask check in the vacm config file check function. Finally, release notes and some cosmetic changes to the agent config-file(s) user guide chapter. --- lib/snmp/src/agent/snmp_view_based_acm_mib.erl | 206 +++++++++++++------------ lib/snmp/src/app/snmp.appup.src | 12 ++ lib/snmp/src/misc/snmp_conf.erl | 35 ++++- 3 files changed, 157 insertions(+), 96 deletions(-) (limited to 'lib/snmp/src') diff --git a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl index 0acef780f8..c0177b1cea 100644 --- a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl +++ b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2012. All Rights Reserved. +%% Copyright Ericsson AB 1999-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -49,6 +49,14 @@ -endif. +-type internal_view_mask() :: null | [internal_view_mask_element()]. +-type internal_view_mask_element() :: 0 | 1. + +-type external_view_mask() :: octet_string(). % At most length of 16 octet +-type octet_string() :: [octet()]. +-type octet() :: byte(). + + %%----------------------------------------------------------------- %% Func: configure/1 %% Args: Dir is the directory where the configuration files are found. @@ -160,14 +168,7 @@ check_vacm({vacmViewTreeFamily, ViewName, Tree, Type, Mask}) -> {ok, TypeVal} = snmp_conf:check_atom(Type, [{included, ?view_included}, {excluded, ?view_excluded}]), - MaskVal = - case (catch snmp_conf:check_atom(Mask, [{null, []}])) of - {error, _} -> - snmp_conf:check_oid(Mask), - Mask; - {ok, X} -> - X - end, + {ok, MaskVal} = snmp_conf:check_imask(Mask), Vacm = {ViewName, Tree, MaskVal, TypeVal, ?'StorageType_nonVolatile', ?'RowStatus_active'}, {ok, {vacmViewTreeFamily, Vacm}}; @@ -194,8 +195,8 @@ init_tabs(Sec2Group, Access, View) -> ok. init_sec2group_table([Row | T]) -> -%% ?vtrace("init security-to-group table: " -%% "~n Row: ~p",[Row]), + %% ?vtrace("init security-to-group table: " + %% "~n Row: ~p",[Row]), Key1 = element(1, Row), Key2 = element(2, Row), Key = [Key1, length(Key2) | Key2], @@ -953,13 +954,23 @@ verify_vacmViewTreeFamilyTable_col(?vacmViewTreeFamilySubtree, Tree) -> wrongValue(?vacmViewTreeFamilySubtree) end; verify_vacmViewTreeFamilyTable_col(?vacmViewTreeFamilyMask, Mask) -> + %% Mask here is in the "external" format. That is, according + %% to the MIB, which means that its an OCTET STRING of max 16 + %% octets. + %% We however store the mask as a list of 1's (exact) and + %% 0's (wildcard), which means we have to convert the mask. case Mask of - null -> []; - [] -> []; + %% The Mask can only have this value if the vacmViewTreeFamilyTable + %% is called locally! + null -> + []; + [] -> + []; _ -> - case (catch check_mask(Mask)) of - ok -> - mask2oid(Mask); + %% Check and convert to our internal format + case check_mask(Mask) of + {ok, IMask} -> + IMask; _ -> wrongValue(?vacmViewTreeFamilyMask) end @@ -975,53 +986,60 @@ verify_vacmViewTreeFamilyTable_col(?vacmViewTreeFamilyType, Type) -> end; verify_vacmViewTreeFamilyTable_col(_, Val) -> Val. + +check_mask(Mask) when is_list(Mask) andalso (length(Mask) =< 16) -> + try + begin + {ok, emask2imask(Mask)} + end + catch + throw:{error, _} -> + {error, {bad_mask, Mask}}; + T:E -> + {error, {bad_mask, Mask, T, E}} + end; +check_mask(BadMask) -> + {error, {bad_mask, BadMask}}. + +-spec emask2imask(EMask :: external_view_mask()) -> + IMask :: internal_view_mask(). + +%% Convert an External Mask (OCTET STRING) to Internal Mask (list of 0 or 1) +emask2imask(EMask) -> + lists:flatten([octet2bits(Octet) || Octet <- EMask]). + +octet2bits(Octet) + when is_integer(Octet) andalso (Octet >= 16#00) andalso (16#FF >= Octet) -> + <> = <>, + [A, B, C, D, E, F, G, H]; +octet2bits(BadOctet) -> + throw({error, {bad_octet, BadOctet}}). + +-spec imask2emask(IMask :: internal_view_mask()) -> + EMask :: external_view_mask(). + +%% Convert an Internal Mask (list of 0 or 1) to External Mask (OCTET STRING) +imask2emask(IMask) -> + imask2emask(IMask, []). + +imask2emask([], EMask) -> + lists:reverse(EMask); +imask2emask(IMask, EMask) -> + %% Make sure we have atleast 8 bits + %% (maybe extend with 1's) + IMask2 = + case length(IMask) of + Small when Small < 8 -> + IMask ++ lists:duplicate(8-Small, 1); + _ -> + IMask + end, + %% Extract 8 bits + [A, B, C, D, E, F, G, H | IMaskRest] = IMask2, + <> = <>, + imask2emask(IMaskRest, [Octet | EMask]). -check_mask([]) -> - ok; -check_mask([Head | Tail]) when is_integer(Head) -> -% we can cause exception here because the caller catches - if - Head > -1 andalso Head < 256 -> - check_mask(Tail) - end. - -% internally (for easier computations), the mask is stored -% as OID and not as bit-field. Therefore, we have to convert -% the bitstring to a list of integers before storing it. -mask2oid(Mask) -> - Func = fun(Byte) -> - % what's a nice syntax for extracting bits - % from a byte??? - <> = <>, - [A, B, C, D, E, F, G, H] - end, - lists:flatten(lists:map(Func, Mask)). - -oid2mask(Oid) -> - % convert all suboids to either 1 or 0 for sure - Func = fun - (0) -> 0; - (_) -> 1 - end, - oid2mask(lists:map(Func, Oid), []). - -oid2mask([], Mask) -> - % end-of-list, return bitstring - lists:reverse(Mask); -oid2mask(Oid, Mask) -> - % check whether we've got at least 8 sub-identifiers. if not, - % extend with 1's until there are enough to fill a byte - NOid = case length(Oid) of - Small when Small < 8 -> - Oid ++ lists:duplicate(8-Small, 1); - _ -> - Oid - end, - % extract sufficient suboids for 8 bits - [A, B, C, D, E, F, G, H | Tail] = NOid, - <> = <>, - oid2mask(Tail, [Byte | Mask]). table_next(Name, RestOid) -> @@ -1061,42 +1079,40 @@ stc(vacmSecurityToGroupTable) -> ?vacmSecurityToGroupStorageType; stc(vacmViewTreeFamilyTable) -> ?vacmViewTreeFamilyStorageType. next(Name, RowIndex, Cols) -> - Ans = snmp_generic:handle_table_next(db(Name), RowIndex, Cols, - fa(Name), foi(Name), noc(Name)), - patch_next(Name, Ans). + Result = snmp_generic:handle_table_next(db(Name), RowIndex, Cols, + fa(Name), foi(Name), noc(Name)), + externalize_next(Name, Result). get(Name, RowIndex, Cols) -> - Ans = snmp_generic:handle_table_get(db(Name), RowIndex, Cols, foi(Name)), - patch(Name, Cols, Ans). - - -patch(Name, Cols, Ans) when is_list(Ans) -> - % function to patch returned values - Func = fun - ({Col, {value, Val}}) -> {value, do_patch(Name, Col, Val)}; - ({_, Other}) -> Other - end, - % merge column numbers and return values. there must be as much - % return values as there are columns requested - Tmp = lists:zip(Cols, Ans), - % and patch all values - lists:map(Func, Tmp); -patch(_, _, Ans) -> - Ans. - -patch_next(Name, Ans) when is_list(Ans) -> - Func = fun - ({[C | _] = Idx, Val}) -> {Idx, do_patch(Name, C, Val)}; - (Other) -> Other - end, - lists:map(Func, Ans); -patch_next(_, Ans) -> - Ans. - -do_patch(vacmViewTreeFamilyTable, ?vacmViewTreeFamilyMask, Val) -> - oid2mask(Val); -do_patch(_, _, Val) -> - Val. + Result = snmp_generic:handle_table_get(db(Name), RowIndex, Cols, + foi(Name)), + externalize_get(Name, Cols, Result). + + +externalize_next(Name, Result) when is_list(Result) -> + F = fun({[Col | _] = Idx, Val}) -> {Idx, externalize(Name, Col, Val)}; + (Other) -> Other + end, + [F(R) || R <- Result]; +externalize_next(_, Result) -> + Result. + + +externalize_get(Name, Cols, Result) when is_list(Result) -> + %% Patch returned values + F = fun({Col, {value, Val}}) -> {value, externalize(Name, Col, Val)}; + ({_, Other}) -> Other + end, + %% Merge column numbers and return values. there must be as much + %% return values as there are columns requested. And then patch all values + [F(R) || R <- lists:zip(Cols, Result)]; +externalize_get(_, _, Result) -> + Result. + +externalize(vacmViewTreeFamilyTable, ?vacmViewTreeFamilyMask, Val) -> + imask2emask(Val); +externalize(_, _, Val) -> + Val. wrongValue(V) -> throw({wrongValue, V}). diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src index 7ffa4a725d..106fed749d 100644 --- a/lib/snmp/src/app/snmp.appup.src +++ b/lib/snmp/src/app/snmp.appup.src @@ -22,6 +22,12 @@ %% ----- U p g r a d e ------------------------------------------------------- [ + {"4.24", + [ + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, [snmp_conf]} + ] + }, {"4.23.1", [{restart_application, snmp}]}, {"4.23", [{restart_application, snmp}]} ], @@ -29,6 +35,12 @@ %% ------D o w n g r a d e --------------------------------------------------- [ + {"4.24", + [ + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, [snmp_conf]} + ] + }, {"4.23.1", [{restart_application, snmp}]}, {"4.23", [{restart_application, snmp}]} ] diff --git a/lib/snmp/src/misc/snmp_conf.erl b/lib/snmp/src/misc/snmp_conf.erl index e1e7fab57b..46625989d5 100644 --- a/lib/snmp/src/misc/snmp_conf.erl +++ b/lib/snmp/src/misc/snmp_conf.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2012. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -50,6 +50,7 @@ check_packet_size/1, check_oid/1, + check_imask/1, check_emask/1, check_mp_model/1, check_sec_model/1, check_sec_model/2, check_sec_model/3, @@ -488,6 +489,7 @@ do_check_timer(WaitFor, Factor, Incr, Retry) -> check_integer(Retry, {gte, 0}), ok. + %% --------- all_domains() -> @@ -616,6 +618,37 @@ check_oid(X) -> error({invalid_object_identifier, X}). +%% --------- + +%% Check a (view) mask in the internal form (all 0 and 1): +check_imask(null) -> + {ok, []}; +check_imask(IMask) when is_list(IMask) -> + do_check_imask(IMask), + {ok, IMask}. + +do_check_imask([0|IMask]) -> + do_check_imask(IMask); +do_check_imask([1|IMask]) -> + do_check_imask(IMask); +do_check_imask([X|_]) -> + error({invalid_internal_mask_element, X}). + + +%% Check a (view) mask in the external form (according to MIB, +%% an OCTET STRING of at most length 16). +check_emask(EMask) when is_list(EMask) andalso (length(EMask) =< 16) -> + do_check_emask(EMask). + +do_check_emask([]) -> + ok; +do_check_emask([X|EMask]) + when is_integer(X) andalso (X >= 16#00) andalso (X =< 16#FF) -> + do_check_emask(EMask); +do_check_emask([X|_]) -> + error({invalid_external_mask_element, X}). + + %% --------- all_integer([H|T]) when is_integer(H) -> all_integer(T); -- cgit v1.2.3