Represents a data packet transferred between RADIUS client and server.
Inheritance: ISupportBinaryImage
Example #1
0
        /// <summary>
        /// Authenticates the username and password against the RADIUS server.
        /// </summary>
        /// <param name="username">Username to be authenticated.</param>
        /// <param name="password">Password to be authenticated.</param>
        /// <param name="state">State value from a previous challenge response.</param>
        /// <returns>Response packet received from the server for the authentication request.</returns>
        /// <remarks>
        /// <para>
        /// The type of response packet (if any) will be one of the following:
        /// <list>
        /// <item>AccessAccept: If the authentication is successful.</item>
        /// <item>AccessReject: If the authentication is not successful.</item>
        /// <item>AccessChallenge: If the server need more information from the user.</item>
        /// </list>
        /// </para>
        /// <para>
        /// When an AccessChallenge response packet is received from the server, it contains a State attribute
        /// that must be included in the AccessRequest packet that is being sent in response to the AccessChallenge
        /// response. So if this method returns an AccessChallenge packet, then this method is to be called again
        /// with the requested information (from ReplyMessage attribute) in the password field and the value State
        /// attribute.
        /// </para>
        /// </remarks>
        public RadiusPacket Authenticate(string username, string password, byte[] state)
        {
            CheckDisposed();

            if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
            {
                throw new ArgumentException("Username and Password cannot be null.");
            }

            RadiusPacket request = new RadiusPacket(PacketType.AccessRequest);

            byte[] authenticator = RadiusPacket.CreateRequestAuthenticator(m_sharedSecret);

            request.Authenticator = authenticator;
            request.Attributes.Add(new RadiusPacketAttribute(AttributeType.UserName, username));
            request.Attributes.Add(new RadiusPacketAttribute(AttributeType.UserPassword, RadiusPacket.EncryptPassword(password, m_sharedSecret, authenticator)));

            // State attribute is used when responding to a AccessChallenge response.
            if ((object)state != null)
            {
                request.Attributes.Add(new RadiusPacketAttribute(AttributeType.State, state));
            }

            return(ProcessRequest(request));
        }
Example #2
0
        /// <summary>
        /// Send a request to the server and waits for a response back.
        /// </summary>
        /// <param name="request">Request to be sent to the server.</param>
        /// <returns>Response packet if a valid response is received from the server; otherwise Nothing.</returns>
        public RadiusPacket ProcessRequest(RadiusPacket request)
        {
            CheckDisposed();

            // We wait indefinitely for the connection to establish. But since this is UDP, the connection
            // will always be successful (locally we're binding to any available UDP port).
            if (m_udpClient.CurrentState != ClientState.Connected)
            {
                return(null);
            }

            RadiusPacket response = null;

            // We have a UDP socket we can use for exchanging packets.
            DateTime stopTime;

            for (int i = 1; i <= m_requestAttempts; i++)
            {
                m_responseBytes = null;
                m_udpClient.Send(request.BinaryImage());

                stopTime = DateTime.UtcNow.AddMilliseconds(m_reponseTimeout);

                while (true)
                {
                    Thread.Sleep(1);

                    // Stay in the loop until:
                    // 1) We receive a response OR
                    // 2) We exceed the response timeout duration
                    if ((object)m_responseBytes != null || DateTime.UtcNow > stopTime)
                    {
                        break;
                    }
                }

                if ((object)m_responseBytes != null)
                {
                    // The server sent a response.
                    response = new RadiusPacket(m_responseBytes, 0, m_responseBytes.Length);

                    if (response.Identifier == request.Identifier && response.Authenticator.CompareTo(RadiusPacket.CreateResponseAuthenticator(m_sharedSecret, request, response)) == 0)
                    {
                        break;
                    }

                    // The response failed the verification, so we'll silently discard it.
                    response = null;
                }
            }

            return(response);
        }
Example #3
0
        /// <summary>
        /// Generates an "Authenticator" value used in a RADIUS response packet sent by the server to client.
        /// </summary>
        /// <param name="sharedSecret">The shared secret key.</param>
        /// <param name="requestPacket">RADIUS packet sent from client to server.</param>
        /// <param name="responsePacket">RADIUS packet sent from server to client.</param>
        /// <returns>A byte array.</returns>
        public static byte[] CreateResponseAuthenticator(string sharedSecret, RadiusPacket requestPacket, RadiusPacket responsePacket)
        {
            // Response authenticator is generated as follows:
            // MD5(Code + Identifier + Length + Request Authenticator + Attributes + Shared Secret)
            //   where:
            //   Code, Identifier, Length & Attributes are from the response RADIUS packet
            //   Request Authenticator is from the request RADIUS packet
            //   Shared Secret is the shared secret key
            int length = responsePacket.BinaryLength;

            byte[] sharedSecretBytes = Encoding.GetBytes(sharedSecret);
            byte[] buffer            = new byte[length + sharedSecretBytes.Length];

            responsePacket.GenerateBinaryImage(buffer, 0);
            Buffer.BlockCopy(requestPacket.BinaryImage(), 4, buffer, 4, 16);
            Buffer.BlockCopy(sharedSecretBytes, 0, buffer, length, sharedSecretBytes.Length);

            return(new MD5CryptoServiceProvider().ComputeHash(buffer));
        }
Example #4
0
        /// <summary>
        /// Determines whether or not the response indicates that the user account is in "Next Token" mode.
        /// </summary>
        /// <param name="response">Response packet sent by the server.</param>
        /// <returns>True if the user account is in "Next Token" mode; otherwise False.</returns>
        /// <remarks>
        /// <para>
        /// A user's account can enter the "Next Token" mode after the user enters incorrect passwords for a few
        /// times (3 times by default) and then enters the correct password. Note that repeatedly entering
        /// incorrect passwords will disable the user account.
        /// </para>
        /// <para>NOTE: This method is specific to RSA RADIUS implementation.</para>
        /// </remarks>
        public bool IsUserInNextTokenMode(RadiusPacket response)
        {
            CheckDisposed();

            if ((object)response == null)
            {
                throw new ArgumentNullException(nameof(response));
            }

            byte[] messageBytes = response.GetAttributeValue(AttributeType.ReplyMessage);

            if ((object)messageBytes == null)
            {
                throw new ArgumentException("ReplyMessage attribute is not present", nameof(response));
            }

            // Unfortunately, the only way of determining whether or not a user account is in the
            // "Next Token" mode is from the text present in the ReplyMessage attribute of the
            // AccessChallenge response from server.
            string messageString = RadiusPacket.Encoding.GetString(messageBytes, 0, messageBytes.Length);

            return(messageString.ToLower().Contains(m_nextTokenModeMessage.ToLower()));
        }
Example #5
0
        /// <summary>
        /// Determines whether or not the response indicates that the user account is in "Next Token" mode.
        /// </summary>
        /// <param name="response">Response packet sent by the server.</param>
        /// <returns>True if the user account is in "Next Token" mode; otherwise False.</returns>
        /// <remarks>
        /// <para>
        /// A user's account can enter the "Next Token" mode after the user enters incorrect passwords for a few
        /// times (3 times by default) and then enters the correct password. Note that repeatedly entering
        /// incorrect passwords will disable the user account.
        /// </para>
        /// <para>NOTE: This method is specific to RSA RADIUS implementation.</para>
        /// </remarks>
        public bool IsUserInNextTokenMode(RadiusPacket response)
        {
            CheckDisposed();

            if ((object)response == null)
                throw new ArgumentNullException("response");

            byte[] messageBytes = response.GetAttributeValue(AttributeType.ReplyMessage);

            if ((object)messageBytes == null)
                throw new ArgumentException("ReplyMessage attribute is not present", "response");

            // Unfortunately, the only way of determining whether or not a user account is in the
            // "Next Token" mode is from the text present in the ReplyMessage attribute of the
            // AccessChallenge response from server.
            string messageString = RadiusPacket.Encoding.GetString(messageBytes, 0, messageBytes.Length);
            return messageString.ToLower().Contains(m_nextTokenModeMessage.ToLower());
        }
Example #6
0
        /// <summary>
        /// Authenticates the username and password against the RADIUS server.
        /// </summary>
        /// <param name="username">Username to be authenticated.</param>
        /// <param name="password">Password to be authenticated.</param>
        /// <param name="state">State value from a previous challenge response.</param>
        /// <returns>Response packet received from the server for the authentication request.</returns>
        /// <remarks>
        /// <para>
        /// The type of response packet (if any) will be one of the following:
        /// <list>
        /// <item>AccessAccept: If the authentication is successful.</item>
        /// <item>AccessReject: If the authentication is not successful.</item>
        /// <item>AccessChallenge: If the server need more information from the user.</item>
        /// </list>
        /// </para>
        /// <para>
        /// When an AccessChallenge response packet is received from the server, it contains a State attribute
        /// that must be included in the AccessRequest packet that is being sent in response to the AccessChallenge
        /// response. So if this method returns an AccessChallenge packet, then this method is to be called again
        /// with the requested information (from ReplyMessage attribute) in the password field and the value State
        /// attribute.
        /// </para>
        /// </remarks>
        public RadiusPacket Authenticate(string username, string password, byte[] state)
        {
            CheckDisposed();

            if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
                throw new ArgumentException("Username and Password cannot be null.");

            RadiusPacket request = new RadiusPacket(PacketType.AccessRequest);
            byte[] authenticator = RadiusPacket.CreateRequestAuthenticator(m_sharedSecret);

            request.Authenticator = authenticator;
            request.Attributes.Add(new RadiusPacketAttribute(AttributeType.UserName, username));
            request.Attributes.Add(new RadiusPacketAttribute(AttributeType.UserPassword, RadiusPacket.EncryptPassword(password, m_sharedSecret, authenticator)));

            // State attribute is used when responding to a AccessChallenge response.
            if ((object)state != null)
                request.Attributes.Add(new RadiusPacketAttribute(AttributeType.State, state));

            return ProcessRequest(request);
        }
Example #7
0
        /// <summary>
        /// Send a request to the server and waits for a response back.
        /// </summary>
        /// <param name="request">Request to be sent to the server.</param>
        /// <returns>Response packet if a valid response is received from the server; otherwise Nothing.</returns>
        public RadiusPacket ProcessRequest(RadiusPacket request)
        {
            CheckDisposed();

            // We wait indefinitely for the connection to establish. But since this is UDP, the connection
            // will always be successful (locally we're binding to any available UDP port).
            if (m_udpClient.CurrentState != ClientState.Connected)
                return null;

            RadiusPacket response = null;

            // We have a UDP socket we can use for exchanging packets.
            DateTime stopTime;

            for (int i = 1; i <= m_requestAttempts; i++)
            {
                m_responseBytes = null;
                m_udpClient.Send(request.BinaryImage());

                stopTime = DateTime.UtcNow.AddMilliseconds(m_reponseTimeout);

                while (true)
                {
                    Thread.Sleep(1);

                    // Stay in the loop until:
                    // 1) We receive a response OR
                    // 2) We exceed the response timeout duration
                    if ((object)m_responseBytes != null || DateTime.UtcNow > stopTime)
                        break;
                }

                if ((object)m_responseBytes != null)
                {
                    // The server sent a response.
                    response = new RadiusPacket(m_responseBytes, 0, m_responseBytes.Length);

                    if (response.Identifier == request.Identifier && response.Authenticator.CompareTo(RadiusPacket.CreateResponseAuthenticator(m_sharedSecret, request, response)) == 0)
                        break;

                    // The response failed the verification, so we'll silently discard it.
                    response = null;
                }
            }

            return response;
        }
        /// <summary>
        /// Generates an "Authenticator" value used in a RADIUS response packet sent by the server to client.
        /// </summary>
        /// <param name="sharedSecret">The shared secret key.</param>
        /// <param name="requestPacket">RADIUS packet sent from client to server.</param>
        /// <param name="responsePacket">RADIUS packet sent from server to client.</param>
        /// <returns>A byte array.</returns>
        public static byte[] CreateResponseAuthenticator(string sharedSecret, RadiusPacket requestPacket, RadiusPacket responsePacket)
        {
            // Response authenticator is generated as follows:
            // MD5(Code + Identifier + Length + Request Authenticator + Attributes + Shared Secret)
            //   where:
            //   Code, Identifier, Length & Attributes are from the response RADIUS packet
            //   Request Authenticator is from the request RADIUS packet
            //   Shared Secret is the shared secret key
            int length = responsePacket.BinaryLength;
            byte[] sharedSecretBytes = Encoding.GetBytes(sharedSecret);
            byte[] buffer = new byte[length + sharedSecretBytes.Length];

            responsePacket.GenerateBinaryImage(buffer, 0);
            Buffer.BlockCopy(requestPacket.BinaryImage(), 4, buffer, 4, 16);
            Buffer.BlockCopy(sharedSecretBytes, 0, buffer, length, sharedSecretBytes.Length);

            return new MD5CryptoServiceProvider().ComputeHash(buffer);
        }