/// <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 reponse is received from the server; otherwise Nothing.</returns> public RadiusPacket ProcessRequest(RadiusPacket request) { CheckDisposed(); RadiusPacket response = null; // We wait indefinately 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) { // 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.Now.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 ((m_responseBytes != null) || DateTime.Now > stopTime) { break; } } if (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) { // The response has passed the verification. break; } else { // 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 ket int length = responsePacket.BinaryLength; byte[] sharedSecretBytes = Encoding.GetBytes(sharedSecret); byte[] buffer = BufferPool.TakeBuffer(length + sharedSecretBytes.Length); try { 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); } finally { if (buffer != null) BufferPool.ReturnBuffer(buffer); } }