// 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();
}
}
}
}