Пример #1
0
        /// <summary>
        /// Handle asynchronous packet reception.
        /// </summary>
        /// <param name="ar">The operation's <see cref="IAsyncResult" /> instance.</param>
        private void OnReceive(IAsyncResult ar)
        {
            RadiusPacket request = null;
            ServerState  state   = null;
            int          cbRecv;

            using (TimedLock.Lock(this))
            {
                // Finish receiving the next request packet

                try
                {
                    cbRecv = sock.EndReceiveFrom(ar, ref remoteEP);
                }
                catch (SocketClosedException)
                {
                    // We'll see this when the RADIUS server instance is closed.
                    // I'm not going to report this to the event log.

                    return;
                }
                catch (Exception e)
                {
                    // I'm going to assume that something really bad has
                    // happened to the socket, log the exception and then
                    // return without initiating another receive.  This
                    // effectively stops the server.

                    SysLog.LogException(e);
                    return;
                }

                // Parse the request.  We're going to initiate the
                // authentication below, outside of the lock.

                try
                {
                    request = new RadiusPacket((IPEndPoint)remoteEP, recvBuf, cbRecv);

                    // Unit tests can use this hook to monitor incoming packets
                    // as well cause them to be ignored.

                    if (DiagnosticHook != null && !DiagnosticHook(this, request))
                    {
                        request = null;
                    }

                    if (request != null && request.Code == RadiusCode.AccessRequest)
                    {
                        state = new ServerState(this);
                    }
                    else
                    {
                        // Ignore all RADIUS requests except for Access-Request

                        request = null;
                    }
                }
                catch (Exception e)
                {
                    SysLog.LogException(e);
                }

                // Initiate reception of the next request

                try
                {
                    sock.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref remoteEP, onReceive, null);
                }
                catch (Exception e)
                {
                    SysLog.LogException(e);
                }
            }

            if (request == null)
            {
                return; // We're ignoring the packet
            }
            // Validate the packet and the NAS

            RadiusNasInfo nasInfo;
            IPAddress     nasIPAddress;

            byte[] nasIdentifier;
            string userName;
            string realm;
            string account;

            byte[] encryptedPassword;
            string password;

            if (!request.GetAttributeAsAddress(RadiusAttributeType.NasIpAddress, out nasIPAddress) &&
                !request.GetAttributeAsBinary(RadiusAttributeType.NasIdentifier, out nasIdentifier))
            {
                // Access-Request packets are required by RFC 2865 to have either a NAS-IP-Address
                // or a NAS-IP-Identifier attribute.  Discard any packets that don't have one
                // of these.

                return;
            }

            if (!request.GetAttributeAsText(RadiusAttributeType.UserName, out userName) ||
                !request.GetAttributeAsBinary(RadiusAttributeType.UserPassword, out encryptedPassword))
            {
                // The User-Name attribute is required by RFC 2865 and this implementation
                // requires a User-Password attribute.  Ignore packets without these.

                return;
            }

            // Parse the realm and account from the user name

            Helper.ParseUserName(realmFormat, userName, out realm, out account);

            // Lookup the NAS shared secret and decrypt the password.

            nasInfo = GetNasInfo(state, request.SourceEP.Address);
            if (nasInfo == null)
            {
                if (defSecret == null)
                {
                    // Not being able to find information about a NAS device could
                    // represent a serious security problem or attack so I'm going
                    // to log this.

                    Log(state, false, RadiusLogEntryType.UnknownNas, realm, account, "RADIUS: Unknown NAS device NAS=[{0}].", request.SourceEP);
                    return;
                }

                nasInfo = new RadiusNasInfo(request.SourceEP.Address, defSecret);
            }

            password = request.DecryptUserPassword(encryptedPassword, nasInfo.Secret);

            // Perform the authentication, compute the response
            // authenticator and then send a response packet.

            RadiusPacket response;

            if (Authenticate(state, realm, account, password))
            {
                Log(state, true, RadiusLogEntryType.Authentication, realm, account,
                    "Authenticated: realm=[{0}] account=[{1}] NAS=[{2}]", realm, account, request.SourceEP);

                response = new RadiusPacket(RadiusCode.AccessAccept, request.Identifier, null,
                                            new RadiusAttribute(RadiusAttributeType.ServiceType, (int)RadiusServiceType.Login));
            }
            else
            {
                Log(state, false, RadiusLogEntryType.Authentication, realm, account,
                    "Authentication Fail: realm=[{0}] account=[{1}] NAS=[{2}]", realm, account, request.SourceEP);

                response = new RadiusPacket(RadiusCode.AccessReject, request.Identifier, null);
            }

            response.ComputeResponseAuthenticator(request, nasInfo.Secret);

            try
            {
                sock.SendTo(response.ToArray(), request.SourceEP);
            }
            catch (SocketClosedException)
            {
                // We'll see this when the RADIUS server instance is closed.
                // I'm not going to report this to the event log.
            }
            catch (Exception e)
            {
                SysLog.LogException(e);
            }
        }