//Processes accounting on logon/logoff public void SessionChange(System.ServiceProcess.SessionChangeDescription changeDescription, pGina.Shared.Types.SessionProperties properties) { m_logger.DebugFormat("SessionChange({0})", properties.Id.ToString()); string username = null; if ((bool)Settings.Store.UseModifiedName) { username = properties.GetTrackedSingle <UserInformation>().Username; } else { username = properties.GetTrackedSingle <UserInformation>().OriginalUsername; } if (changeDescription.Reason == System.ServiceProcess.SessionChangeReason.SessionLogon) { //Create a new unique id for this accounting session and store it String sessionId = Guid.NewGuid().ToString(); lock (sessionIDLock) { sessionIDs.Add(username, sessionId); } //Determine which plugin authenticated the user (if any) PluginActivityInformation pai = properties.GetTrackedSingle <PluginActivityInformation>(); Packet.Acct_AuthenticType authSource = Packet.Acct_AuthenticType.Not_Specified; IEnumerable <Guid> authPlugins = pai.GetAuthenticationPlugins(); Guid LocalMachinePluginGuid = new Guid("{12FA152D-A2E3-4C8D-9535-5DCD49DFCB6D}"); foreach (Guid guid in authPlugins) { if (pai.GetAuthenticationResult(guid).Success) { if (guid == SimpleUuid) { authSource = Packet.Acct_AuthenticType.RADIUS; } else if (guid == LocalMachinePluginGuid) { authSource = Packet.Acct_AuthenticType.Local; } else { authSource = Packet.Acct_AuthenticType.Remote; } break; } } try { RADIUSClient client = GetClient(sessionId); client.startAccounting(username, authSource); } catch (Exception e) { m_logger.Error("Error occurred while starting accounting.", e); } } else if (changeDescription.Reason == System.ServiceProcess.SessionChangeReason.SessionLogoff) { //Check if guid was stored from accounting start request (if not, no point in sending a stop request) string sessionId = null; lock (sessionIDLock) { sessionId = sessionIDs.ContainsKey(username) ? sessionIDs[username] : null; if (sessionId == null) { m_logger.ErrorFormat("Error sending accounting stop request. No guid available for {0}", username); return; } //Remove the session id since we're logging off sessionIDs.Remove(username); } try { RADIUSClient client = GetClient(sessionId); client.stopAccounting(username, Packet.Acct_Terminate_CauseType.User_Request); } catch (Exception e) { m_logger.Error("Error occurred while stopping accounting.", e); return; } } }
//Sends a start accounting request to the RADIUS server, returns true on acknowledge of request public bool startAccounting(string username, Packet.Acct_AuthenticType authType) { //Create accounting request packet Packet accountingRequest = new Packet(Packet.Code.Accounting_Request, identifier, sharedKey); accountingRequest.addAttribute(Packet.AttributeType.User_Name, username); accountingRequest.addAttribute(Packet.AttributeType.Acct_Status_Type, (int)Packet.Acct_Status_TypeType.Start); if (String.IsNullOrEmpty(sessionId)) { throw new RADIUSException("Session ID must be present for accounting."); } accountingRequest.addAttribute(Packet.AttributeType.Acct_Session_Id, sessionId); if (NAS_Identifier == null && NAS_IP_Address == null) { throw new RADIUSException("A NAS_Identifier or NAS_IP_Address (or both) must be supplied."); } if (NAS_IP_Address != null) { accountingRequest.addRawAttribute(Packet.AttributeType.NAS_IP_Address, NAS_IP_Address); } if (NAS_Identifier != null) { accountingRequest.addAttribute(Packet.AttributeType.NAS_Identifier, NAS_Identifier); } if (authType != Packet.Acct_AuthenticType.Not_Specified) { accountingRequest.addAttribute(Packet.AttributeType.Acct_Authentic, (int)authType); } m_logger.DebugFormat("Attempting to send {0} for user {1}", accountingRequest.code, username); for (int retryCt = 0; retryCt <= maxRetries; retryCt++) { foreach (string server in servers) { //Accounting request packet created, sending data... UdpClient client = new UdpClient(server, accountingPort); client.Client.SendTimeout = timeout; client.Client.ReceiveTimeout = timeout; try { client.Send(accountingRequest.toBytes(), accountingRequest.length); //Listen for response, since the server has been specified, we don't need to re-specify server IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0); byte[] respBytes = client.Receive(ref RemoteIpEndPoint); Packet responsePacket = new Packet(respBytes); //Verify packet response is good, authenticator should be MD5(Code+ID+Length+RequestAuth+Attributes+Secret) if (!responsePacket.verifyResponseAuthenticator(accountingRequest.authenticator, sharedKey)) { throw new RADIUSException(String.Format("Received response to accounting request with code: {0}, but an incorrect response authenticator was supplied.", responsePacket.code)); } lastReceievedPacket = responsePacket; client.Close(); m_logger.DebugFormat("Received accounting response: {0} for user {1}", responsePacket.code, username); return(responsePacket.code == Packet.Code.Accounting_Response); } //SocketException is thrown if the server does not respond by end of timeout catch (SocketException se) { m_logger.DebugFormat("Accounting start attempt {0}/{1} using {2} failed. Reason: {3}", retryCt + 1, maxRetries + 1, server, se.Message); } catch (Exception e) { throw new RADIUSException("Unexpected error while trying start accounting.", e); } } } throw new RADIUSException(String.Format("No response from server(s) after {0} tries.", maxRetries + 1)); }