1
%% ``The contents of this file are subject to the Erlang Public License,
2
%% Version 1.1, (the "License"); you may not use this file except in
3
%% compliance with the License. You should have received a copy of the
4
%% Erlang Public License along with this software. If not, it can be
5
%% retrieved via the world wide web at http://www.erlang.org/.
7
%% Software distributed under the License is distributed on an "AS IS"
8
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9
%% the License for the specific language governing rights and limitations
12
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
13
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
14
%% AB. All Rights Reserved.''
18
%%-----------------------------------------------------------------
19
%% This module implements the User Based Security Model for SNMP,
20
%% as defined in rfc2274.
21
%%-----------------------------------------------------------------
27
process_incoming_msg/4, generate_outgoing_msg/5]).
29
-define(SNMP_USE_V3, true).
30
-include("snmp_types.hrl").
31
-include("snmpm_usm.hrl").
32
-include("SNMP-USER-BASED-SM-MIB.hrl").
33
-include("SNMP-USM-AES-MIB.hrl").
34
% -include("SNMPv2-TC.hrl").
36
-define(VMODULE,"M-USM").
37
-include("snmp_verbosity.hrl").
40
%%-----------------------------------------------------------------
42
-define(i32(Int), (Int bsr 24) band 255, (Int bsr 16) band 255, (Int bsr 8) band 255, Int band 255).
43
-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).
50
%%-----------------------------------------------------------------
51
%% Func: process_incoming_msg(Packet, Data, SecParams, SecLevel) ->
52
%% {ok, {SecEngineID, SecName, ScopedPDUBytes, SecData}} |
53
%% {error, Reason} | {error, Reason, ErrorInfo}
54
%% Return value may be throwed.
55
%% Types: Reason -> term()
57
%%-----------------------------------------------------------------
58
process_incoming_msg(Packet, Data, SecParams, SecLevel) ->
60
?vtrace("process_incoming_msg -> [3.2.1] check security parms",[]),
62
case (catch snmp_pdus:dec_usm_security_parameters(SecParams)) of
64
inc(snmpInASNParseErrs),
65
error({parseError, Reason}, []);
71
#usmSecurityParameters{msgAuthoritativeEngineID = MsgAuthEngineID,
72
msgUserName = MsgUserName} = UsmSecParams,
73
?vlog("process_incoming_msg -> [3.2.2]"
75
"~n userName: ~p", [MsgAuthEngineID, MsgUserName]),
78
?vtrace("process_incoming_msg -> [3.2.3-b] check engine id",[]),
79
case snmpm_config:is_usm_engine_id_known(MsgAuthEngineID) of
83
SecData1 = [MsgUserName],
84
error(usmStatsUnknownEngineIDs,
85
?usmStatsUnknownEngineIDs_instance,
86
undefined, [{sec_data, SecData1}])
90
?vtrace("process_incoming_msg -> [3.2.4] retrieve usm user",[]),
92
case snmpm_config:get_usm_user(MsgAuthEngineID, MsgUserName) of
96
SecData2 = [MsgUserName],
97
error(usmStatsUnknownUserNames,
98
?usmStatsUnknownUserNames_instance, %% OTP-3542
99
undefined, [{sec_data, SecData2}])
102
%% 3.2.5 - implicit in following checks
104
?vtrace("process_incoming_msg -> "
105
"[3.2.5 - 3.2.7] authenticate incoming",[]),
106
authenticate_incoming(Packet, UsmSecParams, SecUser, SecLevel),
109
?vtrace("process_incoming_msg -> [3.2.8] decrypt scoped data",[]),
110
ScopedPDUBytes = decrypt(Data, SecUser, UsmSecParams, SecLevel),
113
%% Means that if AuthKey/PrivKey are changed; the old values
115
CachedSecData = {MsgUserName,
116
SecUser#usm_user.auth,
117
SecUser#usm_user.auth_key,
118
SecUser#usm_user.priv,
119
SecUser#usm_user.priv_key},
120
SecName = SecUser#usm_user.sec_name,
121
{ok, {MsgAuthEngineID, SecName, ScopedPDUBytes, CachedSecData}}.
124
authenticate_incoming(Packet, UsmSecParams, UsmUser, SecLevel) ->
126
?vtrace("authenticate incoming: 3.2.6",[]),
127
#usmSecurityParameters{msgAuthoritativeEngineID = MsgAuthEngineID,
128
msgAuthoritativeEngineBoots = MsgAuthEngineBoots,
129
msgAuthoritativeEngineTime = MsgAuthEngineTime,
130
msgAuthenticationParameters = MsgAuthParams} =
132
case snmp_misc:is_auth(SecLevel) of
134
SecName = UsmUser#usm_user.sec_name,
135
case is_auth(UsmUser#usm_user.auth,
136
UsmUser#usm_user.auth_key,
142
MsgAuthEngineTime) of
146
error(usmStatsWrongDigests,
147
?usmStatsWrongDigests_instance, SecName)
155
is_auth(usmNoAuthProtocol, _, _, _, SecName, _, _, _) -> % 3.2.5
156
error(usmStatsUnsupportedSecLevels,
157
?usmStatsUnsupportedSecLevels_instance, SecName);
158
is_auth(AuthProtocol, AuthKey, AuthParams, Packet, SecName,
159
MsgAuthEngineID, MsgAuthEngineBoots, MsgAuthEngineTime) ->
160
case auth_in(AuthProtocol, AuthKey, AuthParams, Packet) of
163
?vtrace("retrieve EngineBoots and EngineTime: 3.2.7",[]),
164
SnmpEngineID = get_engine_id(),
165
?vtrace("SnmpEngineID: ~p",[SnmpEngineID]),
166
case MsgAuthEngineID of
167
SnmpEngineID -> %% 3.2.7a
168
?vtrace("we are authoritative: 3.2.7a",[]),
169
SnmpEngineBoots = get_engine_boots(),
170
?vtrace("SnmpEngineBoots: ~p",[SnmpEngineBoots]),
171
SnmpEngineTime = get_engine_time(),
172
?vtrace("SnmpEngineTime: ~p",[SnmpEngineTime]),
175
SnmpEngineBoots == 2147483647 -> false;
176
MsgAuthEngineBoots /= SnmpEngineBoots -> false;
177
MsgAuthEngineTime + 150 < SnmpEngineTime -> false;
178
MsgAuthEngineTime - 150 > SnmpEngineTime -> false;
184
%% OTP-4090 (OTP-3542)
186
error(usmStatsNotInTimeWindows,
187
?usmStatsNotInTimeWindows_instance,
189
[{securityLevel, 1}]) % authNoPriv
191
_ -> %% 3.2.7b - we're non-authoritative
192
?vtrace("we are non-authoritative: 3.2.7b",[]),
193
SnmpEngineBoots = get_engine_boots(MsgAuthEngineID),
194
?vtrace("SnmpEngineBoots: ~p",[SnmpEngineBoots]),
195
SnmpEngineTime = get_engine_time(MsgAuthEngineID),
196
?vtrace("SnmpEngineTime: ~p",[SnmpEngineTime]),
197
LatestRecvTime = get_engine_latest_time(MsgAuthEngineID),
198
?vtrace("LatestRecvTime: ~p",[LatestRecvTime]),
201
MsgAuthEngineBoots > SnmpEngineBoots -> true;
202
MsgAuthEngineBoots == SnmpEngineBoots,
203
MsgAuthEngineTime > LatestRecvTime -> true;
208
?vtrace("update msgAuthoritativeEngineID: 3.2.7b1",
210
set_engine_boots(MsgAuthEngineID,
212
set_engine_time(MsgAuthEngineID,
214
set_engine_latest_time(MsgAuthEngineID,
220
?vtrace("check if message is outside time window: 3.2.7b2",
224
SnmpEngineBoots == 2147483647 ->
225
{false, [{engine, SnmpEngineID},
227
MsgAuthEngineBoots < SnmpEngineBoots ->
228
{false, [{engine, MsgAuthEngineID},
229
{boots, MsgAuthEngineBoots}]};
230
MsgAuthEngineBoots == SnmpEngineBoots,
231
MsgAuthEngineTime < (SnmpEngineTime - 150) ->
232
{false, [{engine, MsgAuthEngineID},
233
{time, MsgAuthEngineTime}]};
238
?vinfo("not in time window[3.2.7b2]: ~p",
240
error(notInTimeWindow, Reason);
251
decrypt(Data, UsmUser, UsmSecParams, SecLevel) ->
252
case snmp_misc:is_priv(SecLevel) of
254
do_decrypt(Data, UsmUser, UsmSecParams);
259
do_decrypt(Data, #usm_user{sec_name = SecName,
262
#usmSecurityParameters{msgPrivacyParameters = PrivParms}) ->
263
EncryptedPDU = snmp_pdus:dec_scoped_pdu_data(Data),
264
try_decrypt(PrivP, PrivKey, PrivParms, EncryptedPDU, SecName).
266
try_decrypt(usmNoPrivProtocol, _, _, _, SecName) -> % 3.2.5
267
error(usmStatsUnsupportedSecLevels,
268
?usmStatsUnsupportedSecLevels_instance, SecName);
269
try_decrypt(usmDESPrivProtocol,
270
PrivKey, MsgPrivParams, EncryptedPDU, SecName) ->
271
case (catch des_decrypt(PrivKey, MsgPrivParams, EncryptedPDU)) of
272
{ok, DecryptedData} ->
275
error(usmStatsDecryptionErrors,
276
?usmStatsDecryptionErrors, SecName)
278
try_decrypt(usmAesCfb128Protocol,
279
PrivKey, UsmSecParams, EncryptedPDU, SecName) ->
280
case (catch aes_decrypt(PrivKey, UsmSecParams, EncryptedPDU)) of
281
{ok, DecryptedData} ->
284
error(usmStatsDecryptionErrors,
285
?usmStatsDecryptionErrors, SecName)
289
%%-----------------------------------------------------------------
290
%% Func: process_outgoing_msg(Message, SecEngineID, SecName,
291
%% SecData, SecLevel) ->
292
%% {ok, {SecEngineID, SecName, ScopedPDUBytes, SecData}} |
293
%% {error, Reason} | {error, Reason, ErrorInfo}
294
%% Return value may be throwed.
295
%% Types: Reason -> term()
297
%%-----------------------------------------------------------------
298
generate_outgoing_msg(Message, SecEngineID, SecName, SecData, SecLevel) ->
300
?vtrace("generate_outgoing_msg -> entry (3.1.1)",[]),
301
{UserName, AuthProtocol, PrivProtocol, AuthKey, PrivKey} =
304
%% Not a response - read from LCD
305
case snmpm_config:get_usm_user_from_sec_name(SecEngineID,
311
User#usm_user.auth_key,
312
User#usm_user.priv_key};
314
error(unknownSecurityName)
317
%% This means the user at the engine is unknown
318
{MsgUserName, usmNoAuthProtocol, usmNoPrivProtocol, "", ""};
323
?vtrace("generate_outgoing_msg -> (3.1.4)",[]),
324
ScopedPduBytes = Message#message.data,
325
{ScopedPduData, MsgPrivParams} =
326
encrypt(ScopedPduBytes, PrivProtocol, PrivKey, SecLevel),
327
SnmpEngineID = get_engine_id(),
328
?vtrace("SnmpEngineID: ~p (3.1.6)",[SnmpEngineID]),
330
{MsgAuthEngineBoots, MsgAuthEngineTime} =
331
case snmp_misc:is_auth(SecLevel) of
332
false when SecData == [] -> % not a response
334
true when SecEngineID /= SnmpEngineID ->
335
{get_engine_boots(SecEngineID), get_engine_time(SecEngineID)};
337
{get_engine_boots(), get_engine_time()}
340
?vtrace("generate_outgoing_msg -> (3.1.5 - 3.1.7)",[]),
342
#usmSecurityParameters{msgAuthoritativeEngineID = SecEngineID,
343
msgAuthoritativeEngineBoots = MsgAuthEngineBoots,
344
msgAuthoritativeEngineTime = MsgAuthEngineTime,
345
msgUserName = UserName,
346
msgPrivacyParameters = MsgPrivParams},
347
Message2 = Message#message{data = ScopedPduData},
349
?vtrace("generate_outgoing_msg -> (3.1.8)",[]),
350
authenticate_outgoing(Message2, UsmSecParams,
351
AuthKey, AuthProtocol, SecLevel).
354
%% Ret: {ScopedPDU, MsgPrivParams} - both are already encoded as OCTET STRINGs
355
encrypt(Data, PrivProtocol, PrivKey, SecLevel) ->
356
case snmp_misc:is_priv(SecLevel) of
360
case try_encrypt(PrivProtocol, PrivKey, Data) of
361
{ok, ScopedPduData, MsgPrivParams} ->
362
{snmp_pdus:enc_oct_str_tag(ScopedPduData), MsgPrivParams};
366
error(encryptionError)
370
try_encrypt(usmNoPrivProtocol, _PrivKey, _Data) -> % 3.1.2
371
error(unsupportedSecurityLevel);
372
try_encrypt(usmDESPrivProtocol, PrivKey, Data) ->
373
des_encrypt(PrivKey, Data);
374
try_encrypt(usmAesCfb128Protocol, PrivKey, Data) ->
375
aes_encrypt(PrivKey, Data).
377
authenticate_outgoing(Message, UsmSecParams,
378
AuthKey, AuthProtocol, SecLevel) ->
380
case snmp_misc:is_auth(SecLevel) of
382
auth_out(AuthProtocol, AuthKey, Message, UsmSecParams);
384
set_msg_auth_params(Message, UsmSecParams)
386
snmp_pdus:enc_message_only(Message2).
390
%%-----------------------------------------------------------------
391
%% Auth and priv algorithms
392
%%-----------------------------------------------------------------
393
auth_in(AuthProtocol, AuthKey, AuthParams, Packet) ->
394
snmp_usm:auth_in(AuthProtocol, AuthKey, AuthParams, Packet).
396
auth_out(AuthProtocol, AuthKey, Message, UsmSecParams) ->
397
snmp_usm:auth_out(AuthProtocol, AuthKey, Message, UsmSecParams).
399
set_msg_auth_params(Message, UsmSecParams) ->
400
snmp_usm:set_msg_auth_params(Message, UsmSecParams, []).
402
des_encrypt(PrivKey, Data) ->
403
snmp_usm:des_encrypt(PrivKey, Data, fun get_des_salt/0).
405
des_decrypt(PrivKey, MsgPrivParams, EncData) ->
406
snmp_usm:des_decrypt(PrivKey, MsgPrivParams, EncData).
409
SaltInt = snmpm_config:incr_counter(usm_des_salt, 1),
410
EngineBoots = get_engine_boots(),
411
[?i32(EngineBoots), ?i32(SaltInt)].
413
aes_encrypt(PrivKey, Data) ->
414
snmp_usm:aes_encrypt(PrivKey, Data, fun get_aes_salt/0).
416
aes_decrypt(PrivKey, UsmSecParams, EncData) ->
417
#usmSecurityParameters{msgPrivacyParameters = MsgPrivParams,
418
msgAuthoritativeEngineTime = EngineTime,
419
msgAuthoritativeEngineBoots = EngineBoots} =
421
snmp_usm:aes_decrypt(PrivKey, MsgPrivParams, EncData,
422
EngineBoots, EngineTime).
425
SaltInt = snmpm_config:incr_counter(usm_aes_salt, 1),
428
%%-----------------------------------------------------------------
431
{ok, EngineID} = snmpm_config:get_engine_id(),
434
get_engine_boots() ->
435
{ok, Boots} = snmpm_config:get_engine_boots(),
439
{ok, Diff} = snmpm_config:get_engine_time(),
443
%%-----------------------------------------------------------------
444
%% We cache the local values of all non-auth engines we know.
445
%% See section 2.3 (Time Synchronization) of the RFC.
446
%%-----------------------------------------------------------------
447
get_engine_boots(SnmpEngineID) ->
448
{ok, Boots} = snmpm_config:get_usm_eboots(SnmpEngineID),
451
get_engine_time(SnmpEngineID) ->
452
{ok, Diff} = snmpm_config:get_usm_etime(SnmpEngineID),
455
get_engine_latest_time(SnmpEngineID) ->
456
{ok, Time} = snmpm_config:get_usm_eltime(SnmpEngineID),
460
set_engine_boots(SnmpEngineID, EngineBoots) ->
461
snmpm_config:set_usm_eboots(SnmpEngineID, EngineBoots).
463
set_engine_time(SnmpEngineID, EngineTime) ->
464
Diff = snmp_misc:now(sec) - EngineTime,
465
snmpm_config:set_usm_etime(SnmpEngineID, Diff).
467
set_engine_latest_time(SnmpEngineID, EngineTime) ->
468
snmpm_config:set_usm_eltime(SnmpEngineID, EngineTime).
471
%%-----------------------------------------------------------------
473
%%-----------------------------------------------------------------
475
throw({error, Reason}).
477
error(Reason, ErrorInfo) ->
478
throw({error, Reason, ErrorInfo}).
480
error(Variable, Oid, SecName) ->
481
error(Variable, Oid, SecName, []).
482
error(Variable, Oid, SecName, Opts) ->
484
ErrorInfo = {#varbind{oid = Oid,
485
variabletype = 'Counter32',
489
throw({error, Variable, ErrorInfo}).
492
%%-----------------------------------------------------------------
495
F = fun(Counter) -> snmpm_config:maybe_cre_stats_counter(Counter, 0) end,
496
lists:map(F, counters()).
499
F = fun(Counter) -> snmpm_config:reset_stats_counter(Counter) end,
500
lists:map(F, counters()).
503
[usmStatsUnsupportedSecLevels,
504
usmStatsNotInTimeWindows,
505
usmStatsUnknownUserNames,
506
usmStatsUnknownEngineIDs,
507
usmStatsWrongDigests,
508
usmStatsDecryptionErrors].
510
inc(Name) -> snmpm_config:incr_stats_counter(Name, 1).