/// <summary> /// Handle a full HTTP Request /// </summary> /// <param name="headers">Headers of the request</param> /// <param name="readObject">The client's session data</param> /// <param name="method">The method of the request</param> /// <param name="resource">The requested function</param> /// <param name="requestData">The body of the request</param> private void HandleRequest(List <string> headers, ReadObject readObject, string method, string resource, string requestData) { bool gotTracker = false; //True if client sent tracker in the headers //Check if client sent header foreach (string header in headers) { if (header.StartsWith("Client-Tracker")) { int delimiter = header.IndexOf(':') + 2; //The : and the space => 2 string value = header.Substring(delimiter); if (int.TryParse(value, out int tracker)) { lock (trackerLockObject) { if (trackers.TryGetValue(tracker, out ReadObject tempReadObject)) { readObject = tempReadObject; gotTracker = true; } else { Form1.WriteLine("Client not registered with this ID"); } } } else { Form1.WriteLine("Can't parse client tracker"); } break; } } if (method == "POST" && gotTracker) //POST Functions { if (resource == "stor-pw") //Store password on phone { string userName = null; string password = null; int userNameLength = int.Parse(requestData.Substring(0, 10)); userName = requestData.Substring(10, userNameLength); int passwordLength = int.Parse(requestData.Substring(10 + userNameLength, 10)); password = requestData.Substring(20 + userNameLength, passwordLength); string url = requestData.Substring(20 + userNameLength + passwordLength); if (password != null && userName != null) { Form1.WriteLine($"Requested Password Storage: usr:{userName} ; pwd:{password}; url:{url}"); readObject.urlHash = SessionCrypto.DigestMessage(url); readObject.userName = userName; readObject.password = password; ctx.RequestPasswordStore(url, userName, password); SendMessage(readObject.client, "stor-init"); } } else if (resource == "getpw") //Get password from phone { readObject.urlHash = SessionCrypto.DigestMessage(requestData); getPasswordQueue.Add(readObject.urlHash); ctx.RequestPasswordGet(readObject.urlHash); SendMessage(readObject.client, "ok"); } else if (resource == "getkey") { readObject.urlHash = SessionCrypto.DigestMessage(requestData); getKeyQueue.Add(readObject.urlHash); ctx.RequestKeyGet(readObject.urlHash); SendMessage(readObject.client, "get-ok"); } else if (resource == "pushkey") { int urlLength = int.Parse(requestData.Substring(0, 10)); string url = requestData.Substring(10, urlLength); int tokenLength = int.Parse(requestData.Substring(10 + urlLength, 10)); string token = requestData.Substring(20 + urlLength, tokenLength); readObject.urlHash = SessionCrypto.DigestMessage(url); storeKeyQueue.Add(readObject.urlHash); storeKeyQueue.UpdateExtraData(readObject.urlHash, token); ctx.RequestKeyStore(readObject.urlHash); SendMessage(readObject.client, "push-ok"); Form1.WriteLine($"ReadObject url hashed: {readObject.urlHash}"); } readObject.contentLength = 0; } else if (method == "GET") //Get functions { if (resource == "tracker") //Client requested a new tracker ID { lock (idLockerObject) { readObject.cTracker = clientTracker; SendMessage(readObject.client, clientTracker.ToString()); clientTracker++; } } else if (!gotTracker) //Drop if not setting tracker and tracker is not set { Form1.WriteLine("GET without tracker and not setting tracker"); } else if (resource == "stor-state") //Get the state of storing the password on the phone { CredentialTransferState state = (storeQueue.TryGetValue(readObject.urlHash, out CredentialTransferState cts)) ? cts : CredentialTransferState.Pending; string message; switch (state) { case CredentialTransferState.Success: message = "stor-completed"; break; case CredentialTransferState.Failed: message = "stor-fail"; break; default: message = "stor-pending"; break; } SendMessage(readObject.client, message); Form1.WriteLine("Stor get state"); } else if (resource == "get-state") //Get the state of getting hte password from the phone { CredentialTransferState qState = getPasswordQueue.GetQueueState(readObject.urlHash); switch (qState) { case CredentialTransferState.Success: object[] extraData = getPasswordQueue.GetQueueData(readObject.urlHash); Tuple <string, string> credentials = new Tuple <string, string>((string)extraData[0], (string)extraData[1]); getPasswordQueue.Remove(readObject.urlHash); SendMessage(readObject.client, $"get-ok{EncodeBodyData(new string[] { credentials.Item1, credentials.Item2 })}"); Form1.WriteLine("Password sent to browser"); break; case CredentialTransferState.Failed: SendMessage(readObject.client, "get-fail"); getPasswordQueue.Remove(readObject.urlHash); Form1.WriteLine("Password fail alert sent to the browser"); break; default: SendMessage(readObject.client, "get-pending"); break; } } else if (resource == "kget-state") { CredentialTransferState qState = getKeyQueue.GetQueueState(readObject.urlHash); switch (qState) { case CredentialTransferState.Success: object[] extraData = getKeyQueue.GetQueueData(readObject.urlHash); string keyToken = (string)extraData[0]; getKeyQueue.Remove(readObject.urlHash); SendMessage(readObject.client, $"get-ok{keyToken}"); Form1.WriteLine("Key sent to browser"); break; case CredentialTransferState.Failed: SendMessage(readObject.client, "get-fail"); getKeyQueue.Remove(readObject.urlHash); Form1.WriteLine("Key fail alert sent to the browser"); break; default: SendMessage(readObject.client, "get-pending"); break; } } else if (resource == "kpush-state") { Form1.WriteLine($"ReadObject url hash: {readObject.urlHash}"); Form1.WriteLine($"ReadObject client tracker: {readObject.cTracker}"); CredentialTransferState qState = storeKeyQueue.GetQueueState(readObject.urlHash); switch (qState) { case CredentialTransferState.Success: object[] extraData = storeKeyQueue.GetQueueData(readObject.urlHash); storeKeyQueue.Remove(readObject.urlHash); SendMessage(readObject.client, $"push-ok"); Form1.WriteLine("Key installed on android"); break; case CredentialTransferState.Failed: SendMessage(readObject.client, "push-fail"); storeKeyQueue.Remove(readObject.urlHash); Form1.WriteLine("Key install failed in android"); break; default: SendMessage(readObject.client, "push-pending"); break; } } readObject.contentLength = 0; } else if (method == "OPTIONS") { SendMessage(readObject.client, "", "\r\nAccess-Control-Allow-Headers: Client-Tracker"); Form1.WriteLine("Options response sent"); readObject.contentLength = 0; return; } else if (!gotTracker) //POST Request (or other?) without tracker { Form1.WriteLine("Tracker not sent with!"); readObject.contentLength = 0; return; } else //Invalid request but tracker is set? wierd { Form1.WriteLine("Invalid Request Type"); readObject.contentLength = 0; return; } //Store the session data lock (trackerLockObject) { if (trackers.ContainsKey(readObject.cTracker)) { trackers[readObject.cTracker] = readObject; } else { trackers.Add(readObject.cTracker, readObject); } } }
/// <summary> /// Read messages from the device /// </summary> /// <param name="ar">Async Result</param> private void ReadCallback(IAsyncResult ar) { int bytesRead = 0; bool noRead = false; ReadObject readObject = (ReadObject)ar.AsyncState; try { bytesRead = readObject.client.EndReceive(ar); } catch (Exception ex) { ServerError?.Invoke(ex); return; } if (bytesRead > 0) { if (readObject.isFirstMessage) { string derPublicKey = keyGeneration.GetDerPublicKey(); SendString(readObject.client, derPublicKey); Form1.WriteLine("Public Key Sent"); readObject.isFirstMessage = false; } else { byte[] dataReceived = new byte[bytesRead]; Array.Copy(readObject.buffer, dataReceived, bytesRead); string textData = Encoding.UTF8.GetString(dataReceived); if (textData.StartsWith("set-session-key")) { Form1.WriteLine("Got set session key"); string encryptedSessionKey = textData.Substring(15); string sessionKey = Encoding.UTF8.GetString(keyGeneration.DecryptData(Convert.FromBase64String(encryptedSessionKey))); readObject.session.SetSessionKey(sessionKey); } else { Form1.WriteLine("Got session message"); byte[] sessionData = readObject.session.DecryptData(textData); string sessionTextData = Encoding.UTF8.GetString(sessionData); Form1.WriteLine($"Session Data: {sessionTextData}"); if (sessionTextData.StartsWith("res-")) { Form1.WriteLine("Got request setter"); readObject.requestString = sessionTextData.Substring(sessionTextData.IndexOf('-') + 1); Form1.WriteLine($"Request string set to: {readObject.requestString}"); SendString(readObject.client, Convert.ToBase64String(readObject.session.EncryptData(Encoding.UTF8.GetBytes("no-data")))); Form1.WriteLine("Continue message sent to client"); } else if (sessionTextData.StartsWith("no-")) { Form1.WriteLine($"Couldn't find credentials on the device: {sessionTextData}"); string parameter = sessionTextData.Substring(sessionTextData.IndexOf('-') + 1); if (parameter == "setup") { LockStateChanged?.Invoke(LockState.Failed); } else if (parameter == "fp") { Form1.WriteLine("User failed to authenticate with fingerprint!"); if (readObject.requestString == "getkname-windowsLogon") { WindowsLocking.CancelRegistration(); } else if (readObject.requestString.StartsWith("pw-")) { Form1.WriteLine("Password get failed"); if (jsSrv != null) { jsSrv.getPasswordQueue.UpdateState(readObject.requestString.Substring(3), JavascriptServer.CredentialTransferState.Failed); } } else if (readObject.requestString.StartsWith("storpw-")) { Form1.WriteLine("Password storage failed"); if (jsSrv != null) { jsSrv.getPasswordQueue.UpdateState(readObject.requestString.Substring(7), JavascriptServer.CredentialTransferState.Failed); } } else if (readObject.requestString.StartsWith("getkname-")) { Form1.WriteLine("Get key failed"); if (jsSrv != null) { jsSrv.getPasswordQueue.UpdateState(readObject.requestString.Substring(10), JavascriptServer.CredentialTransferState.Failed); } } } noRead = true; } else if (sessionTextData == "getuser") { if (jsSrv == null) { throw new InvalidOperationException("Can't store password while javascript server is down"); } Tuple <string, string> creds = jsSrv.GetCredentials(readObject.requestString.Substring(7)); if (creds == null) { throw new NullReferenceException("Can't find credentials for the specified web site hash"); } readObject.credentials.username = creds.Item1; readObject.credentials.password = creds.Item2; SendString(readObject.client, Convert.ToBase64String(readObject.session.EncryptData(Encoding.UTF8.GetBytes(readObject.credentials.username)))); } else if (sessionTextData == "getpass") { SendString(readObject.client, Convert.ToBase64String(readObject.session.EncryptData(Encoding.UTF8.GetBytes(readObject.credentials.password)))); if (jsSrv != null) { jsSrv.getPasswordQueue.UpdateState(readObject.requestString.Substring(7), JavascriptServer.CredentialTransferState.Success); } DismissIfOpen?.Invoke(); noRead = true; } else if (sessionTextData.StartsWith("usresp-")) { string parameter = sessionTextData.Substring(sessionTextData.IndexOf('-') + 1); readObject.credentials.username = parameter; Form1.WriteLine($"Got credentials: usr: {readObject.credentials.username} ; pwd: {readObject.credentials.password}"); if (jsSrv == null) { throw new NullReferenceException("Javascript Server was down when trying to get credentials data"); } jsSrv.getPasswordQueue.UpdateExtraData(readObject.requestString.Substring(3), readObject.credentials.username, readObject.credentials.password); jsSrv.getPasswordQueue.UpdateState(readObject.requestString.Substring(3), JavascriptServer.CredentialTransferState.Success); noRead = true; } else if (sessionTextData.StartsWith("pushkey-")) { string key = sessionTextData.Substring(sessionTextData.IndexOf('-') + 1); if (readObject.requestString == "win") { if (WindowsLocking.IsLogonValid(key)) { LockStateChanged?.Invoke(LockState.Unlocked); } else { LockStateChanged?.Invoke(LockState.Failed); } } else if (readObject.requestString == "lock") { if (WindowsLocking.IsLogonValid(key)) { LockStateChanged?.Invoke(LockState.Locked); } else { LockStateChanged?.Invoke(LockState.Failed); } } else { if (jsSrv == null) { throw new NullReferenceException("Javascript server is down when trying to forward 2fa key"); } jsSrv.getKeyQueue.UpdateExtraData(readObject.requestString.Substring(10), key); jsSrv.getKeyQueue.UpdateState(readObject.requestString.Substring(10), JavascriptServer.CredentialTransferState.Success); } noRead = true; } else if (sessionTextData.StartsWith("pwresp-")) { string parameter = sessionTextData.Substring(sessionTextData.IndexOf('-') + 1); readObject.credentials.password = parameter; DismissIfOpen?.Invoke(); } else if (sessionTextData == "getkvalue") { string parameter = readObject.requestString.Substring(readObject.requestString.IndexOf('-') + 1); string pushKey; if (parameter == "windowsLogon") //Windows Lock Authentication { string lockingKey = WindowsLocking.RegisterLogon(); pushKey = lockingKey; } else { if (jsSrv == null) { throw new NullReferenceException("Javascript server was down when trying to send key to android device"); } if (jsSrv.storeKeyQueue.Contains(readObject.requestString.Substring(9))) { pushKey = (string)jsSrv.storeKeyQueue.GetQueueData(readObject.requestString.Substring(9))[0]; } else { pushKey = "fail"; } } if (pushKey == null) { pushKey = "fail"; } SendString(readObject.client, Convert.ToBase64String(readObject.session.EncryptData(Encoding.UTF8.GetBytes(pushKey)))); if (pushKey == "fail") { Form1.WriteLine("PushKey set to fail!"); jsSrv.storeKeyQueue.UpdateState(readObject.requestString.Substring(9), JavascriptServer.CredentialTransferState.Failed); } else { jsSrv.storeKeyQueue.UpdateState(readObject.requestString.Substring(9), JavascriptServer.CredentialTransferState.Success); } noRead = true; } } } } Array.Clear(readObject.buffer, 0, readObject.buffer.Length); if (!noRead) { readObject.client.BeginReceive(readObject.buffer, 0, readObject.buffer.Length, SocketFlags.None, new AsyncCallback(ReadCallback), readObject); } }
/// <summary> /// Read data from the browser request /// </summary> /// <param name="ar">Async Result Object</param> private void ReadCallback(IAsyncResult ar) { int bytesRead = -1; ReadObject readObject = (ReadObject)ar.AsyncState; try { bytesRead = readObject.client.EndReceive(ar); } catch (Exception) { throw; } if (bytesRead > 0) { string textData = Encoding.ASCII.GetString(readObject.buffer, 0, bytesRead); //Get the HTTP Request if (readObject.http.firstData) //First request data { readObject.http.firstData = false; //Next request isn't first string[] dataLines = textData.Split(new String[] { "\r\n" }, StringSplitOptions.None); //Get the lines of the request string requestLine = ""; readObject.http.headers = new List <string>(); readObject.http.requestData = ""; bool addRequestData = false; //True if request body should be created foreach (string line in dataLines) { if (requestLine == "") { requestLine = line; //First line is always request line } else if (line == "") { addRequestData = true; //Empty line means end of headers } else if (!addRequestData) { readObject.http.headers.Add(line); //Add headers if not ended } else if (addRequestData) { readObject.http.requestData += line; //Append request body if headers ended } } //Parse the content length readObject.http.contentLength = -1; foreach (string header in readObject.http.headers) { if (header.StartsWith("Content-Length: ")) { int delimiter = header.IndexOf(':') + 2; readObject.http.contentLength = int.Parse(header.Substring(delimiter)); } } string[] reqLineParts = requestLine.Split(' '); readObject.http.method = reqLineParts[0]; //Set the request method readObject.http.resource = reqLineParts[1].Substring(1); //Set the requested URL } else //Request fragmented, append the request body { readObject.http.requestData += textData; } if (readObject.http.contentLength != -1 && ((readObject.http.contentLength > 0 && readObject.http.requestData == "") || (readObject.http.contentLength != readObject.http.requestData.Length))) //Browser didn't send the request (full) body in this message { Form1.WriteLine("Request fragmented"); } else //The request is fully appended { Form1.WriteLine("Request completed"); HandleRequest(readObject.http.headers, readObject, readObject.http.method, readObject.http.resource, readObject.http.requestData); //Handle the request readObject.http = new HTTPRequest() { firstData = true }; //Reset the request related data Form1.WriteLine("Request handled"); } Array.Clear(readObject.buffer, 0, readObject.buffer.Length); //Clear receive buffer readObject.client.BeginReceive(readObject.buffer, 0, readObject.buffer.Length, SocketFlags.None, new AsyncCallback(ReadCallback), readObject); //Receive again from the stream } }
/// <summary> /// Start the Locking UI /// </summary> internal void StartLockForm() { lockForm = new Form { FormBorderStyle = FormBorderStyle.None, WindowState = FormWindowState.Normal, Text = "FP Auth Lock Window", StartPosition = FormStartPosition.Manual, BackColor = Color.White }; qrCodePlaceholder = new PictureBox { SizeMode = PictureBoxSizeMode.AutoSize, }; lockForm.Controls.Add(qrCodePlaceholder); #if SafetyButton AddSafetyButton(lockForm); #endif #if ForceTopDisable Form1.WriteLine("Foreground Protection Enable Supressed"); #else protectionCancel = new CancellationTokenSource(); StartForegroundProtection(lockForm); #endif Screen primary = Screen.PrimaryScreen; lockForm.SetBounds(primary.Bounds.X, primary.Bounds.Y, primary.Bounds.Width, primary.Bounds.Height); Action showForm = new Action(() => lockForm.Show()); if (Form1.me.InvokeRequired) { Form1.me.Invoke(showForm); } else { showForm(); } screenBlockers.Clear(); foreach (Screen desktop in Screen.AllScreens) { if (desktop == primary) { continue; } Form screenLocker = new Form { StartPosition = FormStartPosition.Manual, FormBorderStyle = FormBorderStyle.None, BackColor = Color.White }; screenLocker.SetBounds(desktop.Bounds.X, desktop.Bounds.Y, desktop.Bounds.Width, desktop.Bounds.Height); Label l = new Label { Text = "Computer Is Locked, and this screen is blocked from using it, until not logged in", AutoSize = false, TextAlign = ContentAlignment.MiddleCenter, Dock = DockStyle.Fill }; Font f = new Font(FontFamily.GenericSansSerif, 12); l.Font = f; screenLocker.Controls.Add(l); Action showBlocker = new Action(() => screenLocker.Show()); if (Form1.me.InvokeRequired) { Form1.me.Invoke(showBlocker); } else { showBlocker(); } #if ForceTopDisable Form1.WriteLine("Foreground Protection Enable Supressed"); #else StartForegroundProtection(screenLocker); #endif screenBlockers.Add(screenLocker); } }