// /** // * 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 radiusAttributes = new List(); 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() 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; } } }