// Secure SNMP context class. // Copyright (C) 2009-2010 Lex Li // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons // to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; #if NET471 using System.Configuration; #endif using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Net; using GSF.Net.Snmp.Messaging; using GSF.Net.Snmp.Security; namespace GSF.Net.Snmp.Pipeline { /// /// Secure SNMP context. It is specific to v3. /// internal sealed class SecureSnmpContext : SnmpContextBase { /// /// Initializes a new instance of the class. /// /// The request. /// The sender. /// The users. /// The engine core group. /// The binding. public SecureSnmpContext(ISnmpMessage request, IPEndPoint sender, UserRegistry users, EngineGroup group, IListenerBinding binding) : base(request, sender, users, group, binding) { } private void HandleFailure(Variable failure) { IPrivacyProvider defaultPair = DefaultPrivacyProvider.DefaultPair; int[] time = Group.EngineTimeData; Response = new ReportMessage( Request.Version, new Header( new Integer32(Request.MessageId()), new Integer32(Messenger.MaxMessageSize), 0), // no need to encrypt. new SecurityParameters( Group.EngineId, new Integer32(time[0]), new Integer32(time[1]), Request.Parameters.UserName, defaultPair.AuthenticationProvider.CleanDigest, defaultPair.Salt), new Scope( Group.EngineId, OctetString.Empty, new ReportPdu( Request.RequestId(), ErrorCode.NoError, 0, new List(1) { failure })), defaultPair, null); if (TooBig) { GenerateTooBig(); } } public override void CopyRequest(ErrorCode status, int index) { OctetString userName = Request.Parameters.UserName; IPrivacyProvider privacy = Users.Find(userName); int[] time = Group.EngineTimeData; Response = new ResponseMessage( Request.Version, new Header( new Integer32(Request.MessageId()), new Integer32(Messenger.MaxMessageSize), privacy.ToSecurityLevel()), new SecurityParameters( Group.EngineId, new Integer32(time[0]), new Integer32(time[1]), userName, privacy.AuthenticationProvider.CleanDigest, privacy.Salt), new Scope( Group.EngineId, OctetString.Empty, new ResponsePdu( Request.RequestId(), status, index, Request.Pdu().Variables)), privacy, true, null); if (TooBig) { GenerateTooBig(); } } /// /// Generates too big message. /// public override void GenerateTooBig() { OctetString userName = Request.Parameters.UserName; IPrivacyProvider privacy = Users.Find(userName); int[] time = Group.EngineTimeData; Response = new ResponseMessage( Request.Version, new Header( new Integer32(Request.MessageId()), new Integer32(Messenger.MaxMessageSize), privacy.ToSecurityLevel()), new SecurityParameters( Group.EngineId, new Integer32(time[0]), new Integer32(time[1]), userName, privacy.AuthenticationProvider.CleanDigest, privacy.Salt), new Scope( Group.EngineId, OctetString.Empty, new ResponsePdu( Request.RequestId(), ErrorCode.TooBig, 0, Request.Pdu().Variables)), privacy, true, null); if (TooBig) { Response = null; // TODO: snmpSilentDrops++; } } /// /// Handles the membership. /// /// [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Reviewed. Suppression is OK here.")] public override bool HandleMembership() { ISnmpMessage request = Request; SecurityParameters parameters = request.Parameters; SnmpType typeCode = Request.TypeCode(); if (typeCode == SnmpType.Unknown) { HandleFailure(Group.DecryptionError); return false; } if (typeCode != SnmpType.TrapV2Pdu && parameters.EngineId != Group.EngineId) { HandleDiscovery(); return true; } IPrivacyProvider user = Users.Find(parameters.UserName); if (user == null) { HandleFailure(Group.UnknownSecurityName); return false; } if (typeCode == SnmpType.TrapV2Pdu && (user.EngineIds == null || !user.EngineIds.Contains(parameters.EngineId))) { HandleFailure(Group.UnknownEngineId); return false; } if (parameters.IsInvalid) { HandleFailure(Group.AuthenticationFailure); return false; } if (typeCode == SnmpType.TrapV2Pdu) { return true; } if ((user.ToSecurityLevel() | Levels.Reportable) != request.Header.SecurityLevel) { HandleFailure(Group.UnsupportedSecurityLevel); return false; } bool inTime = EngineGroup.IsInTime(Group.EngineTimeData, parameters.EngineBoots.ToInt32(), parameters.EngineTime.ToInt32()); if (!inTime) { HandleFailure(Group.NotInTimeWindow); return false; } return true; } private static bool TimeIncluded => true; private void HandleDiscovery() { // discovery message received. int[] time = Group.EngineTimeData; Response = new ReportMessage( VersionCode.V3, new Header( new Integer32(Request.MessageId()), new Integer32(Messenger.MaxMessageSize), 0), // no need to encrypt for discovery. new SecurityParameters( Group.EngineId, TimeIncluded ? new Integer32(time[0]) : Integer32.Zero, TimeIncluded ? new Integer32(time[1]) : Integer32.Zero, OctetString.Empty, OctetString.Empty, OctetString.Empty), new Scope( Group.EngineId, OctetString.Empty, new ReportPdu( Request.RequestId(), ErrorCode.NoError, 0, new List(1) { Group.UnknownEngineId })), DefaultPrivacyProvider.DefaultPair, null); if (TooBig) { GenerateTooBig(); } } /// /// Generates the response. /// /// The variables. public override void GenerateResponse(IList variables) { OctetString userName = Request.Parameters.UserName; IPrivacyProvider privacy = Users.Find(userName); int[] time = Group.EngineTimeData; Response = new ResponseMessage( Request.Version, new Header( new Integer32(Request.MessageId()), new Integer32(Messenger.MaxMessageSize), privacy.ToSecurityLevel()), new SecurityParameters( Group.EngineId, new Integer32(time[0]), new Integer32(time[1]), userName, privacy.AuthenticationProvider.CleanDigest, privacy.Salt), new Scope( Group.EngineId, OctetString.Empty, new ResponsePdu( Request.RequestId(), ErrorCode.NoError, 0, variables)), privacy, true, null); if (TooBig) { GenerateTooBig(); } } } }