/// <summary> /// authenticate the user against the enterprise active directory and list the servers available to the user /// </summary> private void CreateEnterpriseSessionFromLogin() { try { // authenticate the user against the enterprise active directory _enterpriseSession = _enterpriseClient.Authenticate(user.Value, password.Value); if (_enterpriseSession.AuthenticationErrorCode != EnterpriseAuthenticationErrorCode.NONE) { if (_enterpriseSession.AuthenticationErrorCode == EnterpriseAuthenticationErrorCode.PASSWORD_EXPIRED) { Page.ClientScript.RegisterClientScriptBlock(this.GetType(), Guid.NewGuid().ToString(), string.Format("openPopup('changePasswordPopup', 'EnterpriseChangePassword.aspx?userId={0}');", user.Value), true); } else { connectError.InnerText = EnterpriseAuthenticationErrorHelper .GetErrorDescription(_enterpriseSession.AuthenticationErrorCode); } UpdateControls(); return; } _enterpriseSession.ClientRemoteIP = ClientIPHelper.ClientIPFromRequest(new HttpContextWrapper(HttpContext.Current).Request, true, new string[] { }); // bind the enterprise session to the current http session HttpContext.Current.Session[HttpSessionStateVariables.EnterpriseSession.ToString()] = _enterpriseSession; // prevent session fixation attack by generating a new session ID upon login // also, using http get method to prevent the browser asking for http post data confirmation if the page is reloaded // https://www.owasp.org/index.php/Session_Fixation Response.Redirect(string.Format("?oldSID={0}", HttpContext.Current.Session.SessionID), true); } catch (ThreadAbortException) { // occurs because the response is ended after redirect } catch (Exception exc) { System.Diagnostics.Trace.TraceError("Failed to create enterprise session from login ({0})", exc); } }
/// <summary> /// enterprise mode from url: load the enterprise session (from querystring param) and proceed to connection; the user is non admin and the url is only usable once /// enterprise mode from login: authenticate the user against the enterprise active directory and list the servers available to the user; the user is admin if member of the "EnterpriseAdminGroup" defined into myrtille services config /// standard mode: connect the specified server; authentication is delegated to the remote server or connection broker (if applicable) /// if MFA is enabled and not already processed, authenticate the user against the configured MFA provider (OTP preferred) /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void ConnectButtonClick( object sender, EventArgs e) { if (!_authorizedRequest) { return; } // one time usage enterprise session url if (_enterpriseSession == null && Request["SI"] != null && Request["SD"] != null && Request["SK"] != null) { CreateEnterpriseSessionFromUrl(); } // MFA (OTP passcode) if (_enterpriseSession == null && _mfaAuthClient.GetState()) { var clientIP = ClientIPHelper.ClientIPFromRequest(new HttpContextWrapper(HttpContext.Current).Request, true, new string[] { }); if (!_mfaAuthClient.Authenticate(user.Value, mfaPassword.Value, clientIP)) { connectError.InnerText = "MFA Authentication failed!"; UpdateControls(); return; } } // enterprise mode from login if (_enterpriseSession == null && _enterpriseClient.GetState()) { CreateEnterpriseSessionFromLogin(); } // connection from: // > standard mode // > enterprise mode: hosts list // > enterprise mode: one time session url else { // the display size is required to start a remote session // if missing, the client will provide it automatically if (string.IsNullOrEmpty(width.Value) || string.IsNullOrEmpty(height.Value)) { return; } // connect if (ConnectRemoteServer()) { // in enterprise mode from login, a new http session id was already generated (no need to do it each time an host is connected!) // in standard mode or enterprise mode from url, a new http session id must be generated if (_enterpriseSession == null || Request["SI"] != null) { // session fixation protection if (_cookielessSession) { // generate a new http session id RemoteSession.OwnerSessionID = HttpSessionHelper.RegenerateSessionId(); } } try { // standard mode: switch to http get (standard login) or remove the connection params from url (auto-connect / start program from url) // enterprise mode: remove the host id from url Response.Redirect("~/", true); } catch (ThreadAbortException) { // occurs because the response is ended after redirect } } // connection failed from the hosts list or from a one time session url else if (_enterpriseSession != null && Request["SD"] != null) { try { // remove the host id from url Response.Redirect("~/", true); } catch (ThreadAbortException) { // occurs because the response is ended after redirect } } } }
/// <summary> /// page load (postback data is now available) /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void Page_Load( object sender, EventArgs e) { // client ip protection if (_clientIPTracking) { var clientIP = ClientIPHelper.ClientIPFromRequest(new HttpContextWrapper(HttpContext.Current).Request, true, new string[] { }); if (Session[HttpSessionStateVariables.ClientIP.ToString()] == null) { Session[HttpSessionStateVariables.ClientIP.ToString()] = clientIP; } else if (!((string)Session[HttpSessionStateVariables.ClientIP.ToString()]).Equals(clientIP)) { System.Diagnostics.Trace.TraceWarning("Failed to validate the client ip"); _authorizedRequest = false; UpdateControls(); return; } } // session spoofing protection if (_cookielessSession) { if (Request.Cookies["clientKey"] == null) { if (Session[HttpSessionStateVariables.ClientKey.ToString()] == null) { var cookie = new HttpCookie("clientKey"); cookie.Value = Guid.NewGuid().ToString(); cookie.Path = "/"; Response.Cookies.Add(cookie); } else { System.Diagnostics.Trace.TraceWarning("Failed to validate the client key: missing key"); _authorizedRequest = false; UpdateControls(); return; } } else { var clientKey = Request.Cookies["clientKey"].Value; if (Session[HttpSessionStateVariables.ClientKey.ToString()] == null) { Session[HttpSessionStateVariables.ClientKey.ToString()] = clientKey; } else if (!((string)Session[HttpSessionStateVariables.ClientKey.ToString()]).Equals(clientKey)) { System.Diagnostics.Trace.TraceWarning("Failed to validate the client key: key mismatch"); _authorizedRequest = false; UpdateControls(); return; } } } // retrieve the active enterprise session, if any if (Session[HttpSessionStateVariables.EnterpriseSession.ToString()] != null) { try { _enterpriseSession = (EnterpriseSession)Session[HttpSessionStateVariables.EnterpriseSession.ToString()]; } catch (Exception exc) { System.Diagnostics.Trace.TraceError("Failed to retrieve the active enterprise session ({0})", exc); } } // retrieve the active remote session, if any if (Session[HttpSessionStateVariables.RemoteSession.ToString()] != null) { try { RemoteSession = (RemoteSession)Session[HttpSessionStateVariables.RemoteSession.ToString()]; if (RemoteSession.State == RemoteSessionState.Disconnected) { // handle connection failure ClientScript.RegisterClientScriptBlock(GetType(), Guid.NewGuid().ToString(), string.Format("handleRemoteSessionExit({0});", RemoteSession.ExitCode), true); // cleanup Session[HttpSessionStateVariables.RemoteSession.ToString()] = null; RemoteSession = null; } } catch (Exception exc) { System.Diagnostics.Trace.TraceError("Failed to retrieve the active remote session ({0})", exc); } } // retrieve a shared remote session from url, if any else if (Request["SSE"] != null) { Session[HttpSessionStateVariables.RemoteSession.ToString()] = GetSharedRemoteSession(Request["SSE"]); try { // remove the shared session guid from url Response.Redirect("~/", true); } catch (ThreadAbortException) { // occurs because the response is ended after redirect } } // postback events may redirect after execution; UI is updated from there if (!IsPostBack) { UpdateControls(); } // disable the browser cache; in addition to a "noCache" dummy param, with current time, on long-polling and xhr requests Response.Cache.SetCacheability(HttpCacheability.NoCache); Response.Cache.SetNoStore(); }
/// <summary> /// page load (postback data is now available) /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void Page_Load( object sender, EventArgs e) { // client ip protection if (_clientIPTracking) { var clientIP = ClientIPHelper.ClientIPFromRequest(new HttpContextWrapper(HttpContext.Current).Request, true, new string[] { }); if (Session[HttpSessionStateVariables.ClientIP.ToString()] == null) { Session[HttpSessionStateVariables.ClientIP.ToString()] = clientIP; } else if (!((string)Session[HttpSessionStateVariables.ClientIP.ToString()]).Equals(clientIP)) { System.Diagnostics.Trace.TraceWarning("Failed to validate the client ip"); _authorizedRequest = false; UpdateControls(); return; } } // session spoofing protection if (_httpSessionUseUri) { if (Request.Cookies[HttpRequestCookies.ClientKey.ToString()] == null) { if (Session[HttpSessionStateVariables.ClientKey.ToString()] == null || _allowShareSessionUrl) { var cookie = new HttpCookie(HttpRequestCookies.ClientKey.ToString()); cookie.Value = Guid.NewGuid().ToString(); cookie.Path = "/"; Response.Cookies.Add(cookie); } else { System.Diagnostics.Trace.TraceWarning("Failed to validate the client key: missing key"); _authorizedRequest = false; UpdateControls(); return; } } else { var clientKey = Request.Cookies[HttpRequestCookies.ClientKey.ToString()].Value; if (Session[HttpSessionStateVariables.ClientKey.ToString()] == null) { Session[HttpSessionStateVariables.ClientKey.ToString()] = clientKey; } else if (!((string)Session[HttpSessionStateVariables.ClientKey.ToString()]).Equals(clientKey) && !_allowShareSessionUrl) { System.Diagnostics.Trace.TraceWarning("Failed to validate the client key: key mismatch"); _authorizedRequest = false; UpdateControls(); return; } } } // retrieve the active enterprise session, if any if (Session[HttpSessionStateVariables.EnterpriseSession.ToString()] != null) { try { _enterpriseSession = (EnterpriseSession)Session[HttpSessionStateVariables.EnterpriseSession.ToString()]; } catch (Exception exc) { System.Diagnostics.Trace.TraceError("Failed to retrieve the active enterprise session ({0})", exc); } } // retrieve the active remote session, if any if (Session[HttpSessionStateVariables.RemoteSession.ToString()] != null) { try { RemoteSession = (RemoteSession)Session[HttpSessionStateVariables.RemoteSession.ToString()]; // if using a connection service, send the connection state if (Session.SessionID.Equals(RemoteSession.OwnerSessionID) && RemoteSession.ConnectionService) { _connectionClient.SetConnectionState(RemoteSession.Id, string.IsNullOrEmpty(RemoteSession.VMAddress) ? RemoteSession.ServerAddress : RemoteSession.VMAddress, GuidHelper.ConvertFromString(RemoteSession.VMGuid), RemoteSession.State); } if (RemoteSession.State == RemoteSessionState.Disconnected) { // if connecting from a login page or url, show any connection failure into a dialog box // otherwise, this is delegated to the connection API used and its related UI if (_loginEnabled) { // handle connection failure var script = string.Format("handleRemoteSessionExit({0});", RemoteSession.ExitCode); // redirect to login page if (!string.IsNullOrEmpty(_loginUrl)) { script += string.Format("window.location.href = '{0}';", _loginUrl); } ClientScript.RegisterClientScriptBlock(GetType(), Guid.NewGuid().ToString(), script, true); } // cleanup Session[HttpSessionStateVariables.RemoteSession.ToString()] = null; if (Session[HttpSessionStateVariables.GuestInfo.ToString()] != null) { Session[HttpSessionStateVariables.GuestInfo.ToString()] = null; } RemoteSession = null; } } catch (Exception exc) { System.Diagnostics.Trace.TraceError("Failed to retrieve the active remote session ({0})", exc); } } // retrieve a shared remote session from url, if any else if (!string.IsNullOrEmpty(Request["gid"])) { var guestId = Guid.Empty; if (Guid.TryParse(Request["gid"], out guestId)) { var sharingInfo = GetSharingInfo(guestId); if (sharingInfo != null) { Session[HttpSessionStateVariables.RemoteSession.ToString()] = sharingInfo.RemoteSession; Session[HttpSessionStateVariables.GuestInfo.ToString()] = sharingInfo.GuestInfo; try { // remove the shared session guid from url Response.Redirect("~/", true); } catch (ThreadAbortException) { // occurs because the response is ended after redirect } } } } if (_httpSessionUseUri) { // if running myrtille into an iframe, the iframe url is registered (into a cookie) after the remote session is connected // this is necessary to prevent a new http session from being generated for the iframe if the page is reloaded, due to the missing http session id into the iframe url (!) // multiple iframes (on the same page), like multiple connections/tabs, requires cookieless="UseUri" for sessionState into web.config // problem is, there can be many cases where the cookie is not removed after the remote session is disconnected (network issue, server down, etc?) // if the page is reloaded, the iframe will use it's previously registered http session... which may not exist anymore or have its active remote session disconnected // if that happens, unregister the iframe url (from the cookie) and reload the page; that will provide a new connection identifier to the iframe and reconnect it if (!string.IsNullOrEmpty(Request["fid"]) && RemoteSession == null) { if (Request.Cookies[Request["fid"]] != null) { // remove the cookie for the given iframe Response.Cookies[Request["fid"]].Expires = DateTime.Now.AddDays(-1); // reload the page ClientScript.RegisterClientScriptBlock(GetType(), Guid.NewGuid().ToString(), "parent.location.href = parent.location.href;", true); } } } // local admin if (_enterpriseSession == null && RemoteSession == null && _enterpriseClient.GetMode() == EnterpriseMode.Local && !string.IsNullOrEmpty(Request["mode"]) && Request["mode"].Equals("admin")) { _localAdmin = true; } // postback events may redirect after execution; UI is updated from there if (!IsPostBack) { UpdateControls(); } // disable the browser cache; in addition to a "noCache" dummy param, with current time, on long-polling and xhr requests Response.Cache.SetCacheability(HttpCacheability.NoCache); Response.Cache.SetNoStore(); }
/// <summary> /// enterprise mode from url: load the enterprise session (from querystring param) and proceed to connection; the user is non admin and the url is only usable once /// enterprise mode from login: authenticate the user against the enterprise active directory and list the servers available to the user; the user is admin if member of the "EnterpriseAdminGroup" defined into myrtille services config /// standard mode: connect the specified server; the rdp authentication is delegated to the rdp server or connection broker (if applicable) /// if MFA is enabled and not already processed, authenticate the user against the configured MFA provider (OTP preferred) /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void ConnectButtonClick( object sender, EventArgs e) { // one time usage enterprise session url if (Request["SI"] != null && Request["SD"] != null && Request["SK"] != null) { CreateEnterpriseSessionFromUrl(); } // MFA (OTP passcode) if (_enterpriseSession == null && _mfaAuthClient.GetState()) { var clientIP = ClientIPHelper.ClientIPFromRequest(new HttpContextWrapper(HttpContext.Current).Request, true, new string[] { }); if (!_mfaAuthClient.Authenticate(user.Value, mfaPassword.Value, clientIP)) { connectError.InnerText = "MFA Authentication failed!"; UpdateControls(); return; } } // enterprise mode from login if (_enterpriseSession == null && _enterpriseClient.GetState()) { CreateEnterpriseSessionFromLogin(); } // connection from: // > standard mode // > enterprise mode: hosts list // > enterprise mode: one time session url else { // the display size is required to start a remote session // if missing, the client will provide it automatically if (string.IsNullOrEmpty(width.Value) || string.IsNullOrEmpty(height.Value)) { return; } // connect if (ConnectRemoteServer()) { try { // in enterprise mode from login, a new http session was already generated (no need to do it each time an host is connected!) // in standard mode or enterprise mode from url, a new http session must be generated if (_enterpriseSession == null || Request["SI"] != null) { // cancel the current http session HttpContext.Current.Session.Abandon(); // prevent session fixation attack by generating a new session ID upon login // also, using http get method to prevent the browser asking for http post data confirmation if the page is reloaded // https://www.owasp.org/index.php/Session_Fixation Response.Redirect(string.Format("?oldSID={0}", HttpContext.Current.Session.SessionID), true); } // remove the host id from url else { Response.Redirect("?", true); } } catch (ThreadAbortException) { // occurs because the response is ended after redirect } } // connection failed from the hosts list or from a one time session url else if (_enterpriseSession != null && Request["SD"] != null) { try { // remove the host id from url Response.Redirect("?", true); } catch (ThreadAbortException) { // occurs because the response is ended after redirect } } } }
/// <summary> /// page load (postback data is now available) /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void Page_Load( object sender, EventArgs e) { // prevent session fixation or stealing SessionFixationHandler(); // retrieve the active enterprise session, if any if (HttpContext.Current.Session[HttpSessionStateVariables.EnterpriseSession.ToString()] != null) { try { _enterpriseSession = (EnterpriseSession)HttpContext.Current.Session[HttpSessionStateVariables.EnterpriseSession.ToString()]; var clientIP = ClientIPHelper.ClientIPFromRequest(new HttpContextWrapper(HttpContext.Current).Request, true, new string[] { }); if (!_enterpriseSession.ClientRemoteIP.Equals(clientIP)) { HttpContext.Current.Session[HttpSessionStateVariables.EnterpriseSession.ToString()] = null; _enterpriseSession = null; } } catch (Exception exc) { System.Diagnostics.Trace.TraceError("Failed to retrieve the enterprise session for the http session {0}, ({1})", HttpContext.Current.Session.SessionID, exc); } } //Retrieve remote session information from session url if (Request.QueryString["SSE"] != null) { RemoteSession = GetSharedRemoteSession(Request.QueryString["SSE"]); if (RemoteSession == null) { Response.Redirect(string.Format("?oldSID={0}", HttpContext.Current.Session.SessionID), true); } } else // retrieve the active remote session, if any if (HttpContext.Current.Session[HttpSessionStateVariables.RemoteSession.ToString()] != null) { try { RemoteSession = (RemoteSession)HttpContext.Current.Session[HttpSessionStateVariables.RemoteSession.ToString()]; } catch (Exception exc) { System.Diagnostics.Trace.TraceError("Failed to retrieve the remote session for the http session {0}, ({1})", HttpContext.Current.Session.SessionID, exc); } } // postback events may redirect after execution; UI is updated from there if (!IsPostBack) { UpdateControls(); } // disable the browser cache; in addition to a "noCache" dummy param, with current time, on long-polling and xhr requests Response.Cache.SetCacheability(HttpCacheability.NoCache); Response.Cache.SetNoStore(); }