diff --git a/RDSFactor/My Project/Settings.Designer.vb b/RDSFactor/My Project/Settings.Designer.vb index b24fb9d..7bf7d9d 100644 --- a/RDSFactor/My Project/Settings.Designer.vb +++ b/RDSFactor/My Project/Settings.Designer.vb @@ -13,15 +13,15 @@ Option Explicit On Namespace My - - _ + + _ Partial Friend NotInheritable Class MySettings Inherits Global.System.Configuration.ApplicationSettingsBase - - Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings()), MySettings) - + + Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings()),MySettings) + #Region "My.Settings Auto-Save Functionality" #If _MyType = "WindowsForms" Then Private Shared addedHandler As Boolean @@ -36,10 +36,10 @@ Namespace My End Sub #End If #End Region - + Public Shared ReadOnly Property [Default]() As MySettings Get - + #If _MyType = "WindowsForms" Then If Not addedHandler Then SyncLock addedHandlerLockObject @@ -57,13 +57,13 @@ Namespace My End Namespace Namespace My - - _ + + _ Friend Module MySettingsProperty - - _ + + _ Friend ReadOnly Property Settings() As Global.RDSFactor.My.MySettings Get Return Global.RDSFactor.My.MySettings.Default diff --git a/RDSFactor/RDSFactor.vb b/RDSFactor/RDSFactor.vb index edae5d1..c742d7b 100644 --- a/RDSFactor/RDSFactor.vb +++ b/RDSFactor/RDSFactor.vb @@ -15,17 +15,14 @@ Public Class RDSFactor Public Shared ADField As String = "" Public Shared ADMailField As String = "" Public Shared EnableOTP As Boolean - - ' Shared key: used for challange encryption - ' TODO: Fix - Public Shared encCode As String = "gewsyy#sjs2!" + Public Shared secrets As NASAuthList Private Shared DEBUG As Boolean Private Shared UserAccessLog As New LogWriter Private Shared Log As New LogWriter - Private secrets As NASAuthList + Private radius1812 As RADIUSServer Private radius1645 As RADIUSServer Private userHash As New Hashtable @@ -160,7 +157,7 @@ Public Class RDSFactor If TSGW = "1" Then handler = New RDSHandler(packet) Else - handler = New CitrixHandler(packet) + 'handler = New CitrixHandler(packet) End If handler.ProcessRequest() @@ -254,7 +251,7 @@ Public Class RDSFactor For i As Integer = 0 To ClientArray.Length - 1 ServerLog("Loading Shared Secret for Client: " & ClientArray(i)) - clientHash.Add(ClientArray(i), EncDec.Decrypt(RConfig.GetKeyValue("Clients", ClientArray(i)), encCode)) + clientHash.Add(ClientArray(i), EncDec.Decrypt(RConfig.GetKeyValue("Clients", ClientArray(i)), "gewsyy#sjs2!")) Next If ConfOk = True Then diff --git a/RDSFactor/RDSFactor.vbproj b/RDSFactor/RDSFactor.vbproj index 1634894..6982b83 100644 --- a/RDSFactor/RDSFactor.vbproj +++ b/RDSFactor/RDSFactor.vbproj @@ -15,7 +15,8 @@ 512 Console v4.0 - Client + + x86 @@ -60,6 +61,7 @@ + @@ -80,7 +82,6 @@ - @@ -137,6 +138,7 @@ + MyApplicationCodeGenerator diff --git a/RDSFactor/app.config b/RDSFactor/app.config new file mode 100644 index 0000000..79551f5 --- /dev/null +++ b/RDSFactor/app.config @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RDSFactor/handlers/CitrixHandler.vb b/RDSFactor/handlers/CitrixHandler.vb deleted file mode 100644 index 9cbff01..0000000 --- a/RDSFactor/handlers/CitrixHandler.vb +++ /dev/null @@ -1,254 +0,0 @@ -Imports System.DirectoryServices -Imports RADAR - -' TODO: I don't use this! It's a leftover, moved out of the way -' from the CICRadarR.vb -' -' Look in RDSHandler how this should be refactored. -Public Class CitrixHandler - - Private mPacket As RADIUSPacket - - Public Sub New(packet As RADIUSPacket) - - End Sub - - Private Sub ProcessPacketCSG(ByVal server As RADIUSServer, ByVal packet As RADIUSPacket) - - ' Let's take a look at just authentication requests, - ' and drop other requests silently ... - - If packet.Code <> RadiusPacketCode.AccessRequest Then - RDSFactor.AccessLog(mPacket, "Not a valid radius packet.. Drop!") - Exit Sub - End If - - - - ' Let's see if we have a username present ... - Dim username As RADIUSAttribute = packet.Attributes.GetFirstAttribute(RadiusAttributeType.UserName) - Dim pass As RADIUSAttribute = packet.Attributes.GetFirstAttribute(RadiusAttributeType.UserPassword) - Dim sid As String = "" - Dim mobile As String = "" - Dim smsCode As String = "" - Dim UserEmail As String = "" - - - ' If an attribute of a particular type is not found, the function - ' will return Nothing. - If username Is Nothing Then - ' Technically, this case is against RFC, so ... drop. - RDSFactor.AccessLog(mPacket, "Not a valid radius packet.. No username pressent.. Drop!") - Exit Sub - End If - - RDSFactor.AccessLog(mPacket, "Processing packet for user: " & username.ToString) - - 'If packetHash.ContainsKey(username.GetString & "_" & pass.GetString) Then - ' Exit Sub - 'End If - - - - Dim existState As Boolean = packet.Attributes.AttributeExists(RadiusAttributeType.State) - RDSFactor.AccessLog(mPacket, "Packet contains a state attribute? State=" & existState.ToString) - If existState = True Then ' Ok we have at packet with the State attribute set. Check if we can identify the authtentication packet. - Dim state As String = packet.Attributes.GetFirstAttribute(RadiusAttributeType.State).ToString - RDSFactor.AccessLog(mPacket, "Packet contains a state attribute State=" & state) - Dim UserDomain As String = "" - 'lets see if user login using upd or UPN name - Dim sUserName As String = username.ToString - Dim sPassword As String = packet.UserPassword - - RDSFactor.AccessLog(mPacket, "SMSToken supplied by user: " & sUserName) - - sid = "" - If InStr(sUserName, "@") > 0 Then 'UPN - UserDomain = sUserName - Else 'UPD - 'read domain from Hashtable - UserDomain = RDSFactor.NetBiosDomain & "\" & sUserName - End If - - sid = EncDec.Encrypt(UserDomain & "_" & packet.UserPassword, RDSFactor.encCode) - RDSFactor.AccessLog(mPacket, "Checking for userHash " & sid) - If sid = state Then - packet.AcceptAccessRequest() - Else - packet.RejectAccessRequest() - End If - Else ' process the first login - - ' packetHash.Add(username.GetString & "_" & pass.GetString, 0) - - ' Console.WriteLine(username.GetString & " is trying to log in ... ") - ' Note that an attribute can represent a string, number, IP, etc. - ' RADAR will not guess that automatically, so use the appropriate - ' function according to the attribute you're trying to read. Otherwise, - ' the Value property is just a bunch of bytes as received in the - ' RADIUS packet. - - - 'Now lets get some information from ad if password is valid - Dim success As Boolean = False - Dim UserDomain As String = "" - 'lets see if user login using upd or UPN name - Dim sUserName As String = username.ToString - Dim sPassword As String = packet.UserPassword - If InStr(sUserName, "@") > 0 Then 'UPN - UserDomain = sUserName - Else 'UPD - 'read domain from Hashtable - UserDomain = RDSFactor.NetBiosDomain & "\" & sUserName - End If - - RDSFactor.AccessLog(mPacket, "User " & UserDomain & " is trying to log in ...") - - - - Try - Dim dirEntry As New DirectoryEntry("LDAP://" & RDSFactor.LDAPDomain, UserDomain, sPassword) - - Dim obj As Object = dirEntry.NativeObject - Dim search As New DirectorySearcher(dirEntry) - - If InStr(sUserName, "@") > 0 Then - search.Filter = "(userPrincipalName=" + sUserName + ")" - Else - search.Filter = "(SAMAccountName=" + sUserName + ")" - End If - 'Load the Properties we need from AD - search.PropertiesToLoad.Add("distinguishedName") - 'search.PropertiesToLoad.Add("primaryTelexNumber") - If RDSFactor.EnableOTP = True Then - If RDSFactor.EnableEmail = True Then - search.PropertiesToLoad.Add(RDSFactor.ADMailField) - End If - If RDSFactor.EnableSMS = True Then - search.PropertiesToLoad.Add(RDSFactor.ADField) - End If - - End If - ' Time to find out if user entered the correct username and pasword - RDSFactor.AccessLog(mPacket, "Trying to authenticate user agains Active Directory using te following parameters: " & "LDAPPAth: " & "LDAP://" & RDSFactor.LDAPDomain & ", Username: " & UserDomain & ", Password: " & sPassword) - - Dim result As SearchResult = search.FindOne() - 'Get the setting form AD. Yes we uses the field primaryTelexNumber, for who the f... still users telex. (I bet half the people reading this code don't even know what a telex is!) - 'Dim code As String = DirectCast(result.Properties("primaryTelexNumber")(0), String) - 'Dim aCode As String() = code.Split("/") - - 'Dim userLdap As String = "LDAP://" & LDAPPath & "/" & result.Properties("distinguishedName")(0) - 'Dim userEntry As New DirectoryEntry(userLdap, UserDomain, sPassword) - If RDSFactor.EnableOTP = True Then - smsCode = RDSFactor.GenerateCode() - - ' REMEMBER to put at check for empty phone string - If RDSFactor.EnableEmail = True Then - Try - UserEmail = DirectCast(result.Properties(RDSFactor.ADMailField)(0), String) - - If UserEmail.Trim.Length = 0 Or InStr(UserEmail, "@") = 0 Then - success = False - RDSFactor.AccessLog(mPacket, "Unable to find correct email for user " & UserDomain) - Else - success = True - End If - Catch - RDSFactor.AccessLog(mPacket, "Unable to find correct email for user " & UserDomain) - success = False - End Try - End If - If RDSFactor.EnableSMS = True Then - Try - mobile = DirectCast(result.Properties(RDSFactor.ADField)(0), String) - mobile = Replace(mobile, "+", "") - If mobile.Trim.Length = 0 Then - success = False - RDSFactor.AccessLog(mPacket, "Unable to find correct phone number for user " & UserDomain) - Else - success = True - End If - Catch - RDSFactor.AccessLog(mPacket, "Unable to find correct phone number for user " & UserDomain) - success = False - End Try - - End If - - sid = EncDec.Encrypt(UserDomain & "_" & smsCode, RDSFactor.encCode) 'generate unique code - End If - ' sid = UserDomain & "_" & smsCode - 'userEntry.Properties("primaryTelexNumber").Value = aCode(0) & "/" & smsCode & "/" & aCode(2) & "/" & aCode(3) - 'userEntry.CommitChanges() - 'userEntry.Dispose() - If 1 = 1 Then ' check if smscode is disabled for the user (Need to write this code) - 'If userHash.ContainsKey(sid) Then - ' userHash(sid) = sPassword - ' If DEBUG = True Then - ' CICRadarR.AccessLog(mPacket, "Updating userHash " & sid) - ' End If - 'Else - ' userHash.Add(sid, sPassword) - ' If DEBUG = True Then - ' CICRadarR.AccessLog(mPacket, "Adding userHash " & sid) - ' End If - 'End If - ' new code stored in AD now send it to the users phone - ' Console.WriteLine(smsCode) - - success = True - Else - success = False - End If - Catch - RDSFactor.AccessLog(mPacket, "Failed to authenticate user agains Active Directory using the following parameters: " & "LDAPPAth: " & "LDAP://" & RDSFactor.LDAPDomain & ", Username: " & UserDomain & ", Password: " & sPassword) - success = False - End Try - - - Dim attributes As New RADIUSAttributes - If success Then ' Yay! Someone guess the password ... - - RDSFactor.AccessLog(mPacket, "User " & UserDomain & " authenticated agains Active Directory") - If RDSFactor.EnableOTP = True Then - Dim attr As New RADIUSAttribute(RadiusAttributeType.ReplyMessage, "SMS Token") - attributes.Add(attr) - Dim state As New RADIUSAttribute(RadiusAttributeType.State, sid) - attributes.Add(state) - ' Console.WriteLine("len " & packet.Authenticator.Length.ToString) - server.SendAsResponse( _ - New RADIUSPacket(RadiusPacketCode.AccessChallenge, _ - packet.Identifier, attributes, _ - packet.EndPoint), _ - packet.Authenticator) - If RDSFactor.EnableSMS = True Then - RDSFactor.AccessLog(mPacket, "Sending access token: " & smsCode & " to phonenumber " & mobile) - Call RDSFactor.SendSMS(mobile, smsCode) - End If - If RDSFactor.EnableEmail = True Then - RDSFactor.AccessLog(mPacket, "Sending access token: " & smsCode & " to email " & UserEmail) - Call RDSFactor.SendEmail(UserEmail, smsCode) - End If - Else - RDSFactor.AccessLog(mPacket, "One time Password not enabled, so we let the user in") - packet.AcceptAccessRequest() - End If - ' packetHash.Remove(username.GetString & "_" & pass.GetString) - Else ' Wrong username / password ... - - RDSFactor.AccessLog(mPacket, "User " & UserDomain & " failed to authenticate against Active Directory") - Dim pk As New RADIUSPacket(RadiusPacketCode.AccessReject, packet.Identifier, Nothing, packet.EndPoint) - server.SendAsResponse(pk, packet.Authenticator) - ' FYI ... if no additional attributes need to be added - ' to the response, you can sepcify Nothing instead of - ' creating an empty RADIUSAttributes object. - ' packetHash.Remove(username.GetString & "_" & pass.GetString) - End If - - - - End If - End Sub - - -End Class diff --git a/RDSFactor/handlers/RDSHandler.vb b/RDSFactor/handlers/RDSHandler.vb index c7529bb..0ec5d9a 100644 --- a/RDSFactor/handlers/RDSHandler.vb +++ b/RDSFactor/handlers/RDSHandler.vb @@ -1,13 +1,20 @@ Imports System.DirectoryServices +Imports System.Web.Helpers Imports RADAR Public Class RDSHandler + ' User -> Token that proves user has authenticated, but not yet proved + ' herself with the 2. factor + Private Shared authTokens As New Hashtable + Private Shared userSessions As New Hashtable Private Shared sessionTimestamps As New Hashtable - Private Shared userSidTokens As New Hashtable + Private Shared encryptedChallangeResults As New Hashtable Private Shared userLaunchTimestamps As New Hashtable + + Private mPacket As RADIUSPacket Private mUsername As String Private mPassword As String @@ -210,27 +217,41 @@ Public Class RDSHandler End Sub Private Sub ProcessChallengeResponse() - RDSFactor.AccessLog(mPacket, "ChallengeResponse") + Dim authToken = mPacket.Attributes.GetFirstAttribute(RadiusAttributeType.State).ToString + If Not authToken = authTokens(mUsername) Then + Throw New Exception("User is trying to respond to challange without valid auth token") + End If - ' When the packet is an Challange-Response the password attr. contains the token - Dim challangeCode = mPassword - Dim state = mPacket.Attributes.GetFirstAttribute(RadiusAttributeType.State) + ' When the packet is an Challange-Response the password attr. contains the encrypted result + Dim userEncryptedResult = mPassword + Dim localEncryptedResult = encryptedChallangeResults(mUsername) - Dim sid = EncDec.Encrypt(mUsername & "_" & challangeCode, RDSFactor.encCode) - If sid = state.ToString Then - userSidTokens.Remove(mUsername) + If localEncryptedResult = userEncryptedResult Then + RDSFactor.AccessLog(mPacket, "ChallengeResponse Success") + encryptedChallangeResults.Remove(mUsername) + authTokens.Remove(mUsername) Accept() Else + RDSFactor.AccessLog(mPacket, "Wrong challange code!") mPacket.RejectAccessRequest() End If End Sub Private Sub TwoFactorChallenge() - Dim code = RDSFactor.GenerateCode - Dim sid = EncDec.Encrypt(mUsername & "_" & code, RDSFactor.encCode) 'generate unique code - RDSFactor.AccessLog(mPacket, "Access Challange Code: " & code) + Dim challangeCode = RDSFactor.GenerateCode + Dim authToken = System.Guid.NewGuid.ToString + Dim clientIP = mPacket.EndPoint.Address.ToString + Dim sharedSecret = RDSFactor.secrets(clientIP) - userSidTokens(mUsername) = sid + RDSFactor.AccessLog(mPacket, "Access Challange Code: " & challangeCode) + + If sharedSecret = Nothing Then + Throw New Exception("No shared secret for client:" & clientIP) + End If + + authTokens(mUsername) = authToken + Dim encryptedChallangeResult = Crypto.SHA256(mUsername & challangeCode & sharedSecret) + encryptedChallangeResults(mUsername) = encryptedChallangeResult If mUseSMSFactor Then RDSFactor.AccessLog(mPacket, "TODO: Send SMS") @@ -242,11 +263,11 @@ Public Class RDSHandler Dim attributes As New RADIUSAttributes - Dim attr As New RADIUSAttribute(RadiusAttributeType.ReplyMessage, "SMS Token") - Dim state As New RADIUSAttribute(RadiusAttributeType.State, sid) + Dim replyMessageAttr As New RADIUSAttribute(RadiusAttributeType.ReplyMessage, "SMS Token") + Dim stateAttr As New RADIUSAttribute(RadiusAttributeType.State, authToken) - attributes.Add(attr) - attributes.Add(state) + attributes.Add(replyMessageAttr) + attributes.Add(stateAttr) mPacket.SendAccessChallange(attributes) End Sub @@ -310,7 +331,8 @@ Public Class RDSHandler userSessions.Remove(username) sessionTimestamps.Remove(username) userLaunchTimestamps.Remove(username) - userSidTokens.Remove(username) + encryptedChallangeResults.Remove(username) + authTokens.Remove(username) End If Next End Sub diff --git a/RDSFactorWeb/RDWeb/Pages/Bin/RADAR.dll b/RDSFactorWeb/RDWeb/Pages/Bin/RADAR.dll index 393a843..1a3e0bb 100644 Binary files a/RDSFactorWeb/RDWeb/Pages/Bin/RADAR.dll and b/RDSFactorWeb/RDWeb/Pages/Bin/RADAR.dll differ diff --git a/RDSFactorWeb/RDWeb/Pages/Bin/RADAR.pdb b/RDSFactorWeb/RDWeb/Pages/Bin/RADAR.pdb index f78a2ca..edc4760 100644 Binary files a/RDSFactorWeb/RDWeb/Pages/Bin/RADAR.pdb and b/RDSFactorWeb/RDWeb/Pages/Bin/RADAR.pdb differ diff --git a/RDSFactorWeb/RDWeb/Pages/Web.config b/RDSFactorWeb/RDWeb/Pages/Web.config index 9582de2..1bb9a1a 100644 --- a/RDSFactorWeb/RDWeb/Pages/Web.config +++ b/RDSFactorWeb/RDWeb/Pages/Web.config @@ -38,9 +38,9 @@ - + - + @@ -85,7 +85,11 @@ - + + + + + @@ -94,12 +98,10 @@ - - diff --git a/RDSFactorWeb/RDWeb/Pages/en-US/smstoken.aspx b/RDSFactorWeb/RDWeb/Pages/en-US/smstoken.aspx index 3e46fb5..3f9da4f 100644 --- a/RDSFactorWeb/RDWeb/Pages/en-US/smstoken.aspx +++ b/RDSFactorWeb/RDWeb/Pages/en-US/smstoken.aspx @@ -2,7 +2,10 @@ <% @Page Language="C#" Debug="false" ResponseEncoding="utf-8" ContentType="text/xml" %> + <% @Import Namespace="System" %> +<% @Import Namespace="System.IO" %> +<% @Import Namespace="System.Web.Helpers" %> <% @Import Namespace="System.Threading" %> <% @Import Namespace="System.Security" %> <% @Import Namespace="Microsoft.TerminalServices.Publishing.Portal.FormAuthentication" %> @@ -71,7 +74,8 @@ void Page_Load(object sender, EventArgs e) { - + logit("pageload"); + btnSignIn.Text = L_SubmitLabel_Text; btnCancel.Text = L_CancelLabel_Text; @@ -87,18 +91,25 @@ RADIUSClient client = new RADIUSClient(RadiusServer, 1812, RadiusSecret); RADIUSAttributes atts = new RADIUSAttributes(); client.Debug = Debug; + try { VendorSpecificAttribute vsa = new VendorSpecificAttribute(VendorSpecificType.Generic,(string)Session["Delivery"]); vsa.SetRADIUSAttribute(ref atts); + logit("AccessReqeust"); + RADIUSPacket response = client.Authenticate(username, strPassword, atts); if (response.Code == RadiusPacketCode.AccessChallenge) { + logit("Challange"); + state = response.Attributes.GetFirstAttribute(RadiusAttributeType.State); Session["State"] = state; } // Access-Accept else if (response.Code == RadiusPacketCode.AccessAccept) { + logit("Accept"); + string sessionGuid = response.Attributes.GetFirstAttribute(RadiusAttributeType.ReplyMessage).GetString(); Session["SESSIONGUID"] = sessionGuid; HttpCookie myCookie = new HttpCookie("RadiusSessionId"); @@ -172,18 +183,26 @@ Response.Redirect(strRedirectSafeUrl,false); } + + public void logit(String message) { + String path = "C:\\RDSFactor\\RDSFactorWeb\\RDWeb\\Pages\\log\\web.log"; + message = DateTime.Now + ": " + message; + using (StreamWriter log = File.AppendText(path)) { + log.WriteLine(message); + } + } protected void btnSignIn_Click(object sender, EventArgs e) { - username = (string)Session["DomainUserName"]; state = (RADIUSAttribute)Session["state"]; - RADIUSClient myRadius = new RADIUSClient(RadiusServer, 1812, RadiusSecret); + RADIUSClient client = new RADIUSClient(RadiusServer, 1812, RadiusSecret); RADIUSAttributes atts = new RADIUSAttributes(); atts.Add(state); - RADIUSPacket response = myRadius.Authenticate(username, SmsToken.Text, atts); + String encryptedChallangeResult = Crypto.SHA256(username + SmsToken.Text + RadiusSecret); + RADIUSPacket response = client.Authenticate(username, encryptedChallangeResult, atts); if (response.Code == RadiusPacketCode.AccessAccept) {