ln.radius/RadiusMessage.cs

187 lines
5.9 KiB
C#

// /**
// * File: RadiusMessage.cs
// * Author: haraldwolff
// *
// * This file and it's content is copyrighted by the Author and / or copyright holder.
// * Any use wihtout proper permission is illegal and may lead to legal actions.
// *
// *
// **/
using System;
using System.Net;
using System.Collections.Generic;
using System.IO;
using ln.types;
using System.Security.Cryptography;
using ln.logging;
namespace ln.radius
{
public enum RadiusCode : byte {
AccessRequest = 1,
AccessAccept = 2,
AccessReject = 3,
AccountingRequest = 4,
AccountingResponse = 5,
AccessChallenge = 11,
StatusServer = 12,
StatusClient = 13,
Reserved = 255
}
public class RadiusMessage
{
public IPEndPoint EndPoint { get; set; }
public RadiusCode Code { get; set; }
byte identifier;
public byte Identifier
{
get => identifier;
set
{
identifier = value;
Authenticator = Guid.NewGuid().ToByteArray();
}
}
public byte[] Authenticator { get; set; }
List<RadiusAttribute> radiusAttributes = new List<RadiusAttribute>();
public RadiusMessage(IPEndPoint endPoint,RadiusCode radiusCode)
{
EndPoint = endPoint;
Code = radiusCode;
}
public RadiusMessage ConstructReply(RadiusCode radiusCode)
{
RadiusMessage reply = new RadiusMessage(EndPoint, radiusCode);
reply.Identifier = Identifier;
reply.Authenticator = Authenticator;
return reply;
}
public RAT GetAttribute<RAT>() where RAT : RadiusAttribute
{
foreach (RadiusAttribute radiusAttribute in radiusAttributes)
if (radiusAttribute is RAT)
return (RAT)radiusAttribute;
return null;
}
public RadiusAttribute GetAttribute(string attrName)
{
foreach (RadiusAttribute radiusAttribute in radiusAttributes)
if (radiusAttribute.Name.Equals(attrName))
return radiusAttribute;
throw new KeyNotFoundException();
}
public void SetAttribute(RadiusAttribute attribute)
{
foreach (RadiusAttribute radiusAttribute in radiusAttributes.ToArray())
{
if (radiusAttribute.Type == attribute.Type)
radiusAttributes.Remove(radiusAttribute);
}
radiusAttributes.Add(attribute);
}
public void AddAttribute(RadiusAttribute attribute)
{
radiusAttributes.Add(attribute);
}
public void RemoveAttribute(RadiusAttribute attribute)
{
radiusAttributes.Remove(attribute);
}
public byte[] Authenticate(byte[] secret)
{
byte[] packet = ToBytes();
MD5 md5 = MD5.Create();
md5.TransformBlock(packet, 0, packet.Length, null, 0);
md5.TransformFinalBlock(secret, 0, secret.Length);
Array.Copy(md5.Hash, 0, packet, 4, 16);
return packet;
}
public bool IsAuthentic(byte[] secret)
{
byte[] authenticator = Authenticator;
bool authentic = false;
try
{
Authenticator = new byte[16];
byte[] packet = ToBytes();
MD5 md5 = MD5.Create();
md5.TransformBlock(packet, 0, packet.Length, null, 0);
md5.TransformFinalBlock(secret, 0, secret.Length);
authentic = authenticator.AreEqual(md5.Hash);
} catch (Exception e)
{
Logging.Log(LogLevel.ERROR, "RadiusMessage.IsAuthentic(): {0}", e);
Logging.Log(e);
}
Authenticator = authenticator;
return authentic;
}
public byte[] ToBytes()
{
using (MemoryStream memoryStream = new MemoryStream())
{
memoryStream.WriteByte((byte)Code);
memoryStream.WriteByte((byte)Identifier);
memoryStream.WriteShort((short)0);
memoryStream.WriteBytes(Authenticator);
foreach (RadiusAttribute radiusAttribute in radiusAttributes)
memoryStream.WriteBytes(radiusAttribute.ToBytes());
byte[] bytes = memoryStream.ToArray();
Array.Copy(
BitConverter.GetBytes((short)bytes.Length).BigEndian(), 0,
bytes, 2,
2
);
return bytes;
}
}
public override string ToString()
{
return String.Format("[RadiusMessage Code={0} Identifier={1} Attributes: {2}]",Code,Identifier,String.Join(", ",radiusAttributes));
}
public static RadiusMessage FromBytes(byte[] bytes,IPEndPoint remoteEndpoint)
{
if ((bytes.Length < 20) || (bytes.Length > 4096))
throw new ArgumentOutOfRangeException(nameof(bytes),"Radius packet needs to be sized between 20 and 4095 bytes");
RadiusMessage radiusMessage = new RadiusMessage(remoteEndpoint, (RadiusCode)bytes[0]);
using (Stream stream = new MemoryStream(bytes))
{
stream.ReadByte();
radiusMessage.Identifier = (byte)stream.ReadByte();
short plength = stream.ReadShort(true);
if (plength != bytes.Length)
throw new ArgumentException("packet: length != bytes.length");
radiusMessage.Authenticator = stream.ReadBytes(16);
while (stream.Position < stream.Length)
radiusMessage.radiusAttributes.Add(RadiusAttribute.Read(stream));
}
return radiusMessage;
}
}
}