Code:
/ FXUpdate3074 / FXUpdate3074 / 1.1 / DEVDIV / depot / DevDiv / releases / whidbey / QFE / ndp / fx / src / xsp / System / Web / Security / FormsAuthentication.cs / 4 / FormsAuthentication.cs
//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//-----------------------------------------------------------------------------
/*
* FormsAuthentication class
*
* Copyright (c) 1999 Microsoft Corporation
*/
namespace System.Web.Security {
using System;
using System.Web;
using System.Text;
using System.Web.Configuration;
using System.Web.Caching;
using System.Collections;
using System.Web.Util;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Threading;
using System.Globalization;
using System.Security.Permissions;
using System.Web.Management;
///
/// This class consists of static methods that
/// provides helper utilities for manipulating authentication tickets.
///
[AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)]
public sealed class FormsAuthentication {
private const int MAC_LENGTH = 20;
private const int MAX_TICKET_LENGTH = 4096;
private static object _lockObject = new object();
internal const string RETURN_URL = "ReturnUrl";
public FormsAuthentication() { }
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Helper functions: Hash a password
///
/// Initializes FormsAuthentication by reading
/// configuration and getting the cookie values and encryption keys for the given
/// application.
///
public static String HashPasswordForStoringInConfigFile(String password, String passwordFormat) {
if (password == null) {
throw new ArgumentNullException("password");
}
if (passwordFormat == null) {
throw new ArgumentNullException("passwordFormat");
}
HashAlgorithm hashAlgorithm;
if (StringUtil.EqualsIgnoreCase(passwordFormat, "sha1"))
hashAlgorithm = SHA1.Create();
else if (StringUtil.EqualsIgnoreCase(passwordFormat, "md5"))
hashAlgorithm = MD5.Create();
else
throw new ArgumentException(SR.GetString(SR.InvalidArgumentValue, "passwordFormat"));
return MachineKeySection.ByteArrayToHexString(hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(password)), 0);
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Initialize this
///
/// Initializes FormsAuthentication by reading
/// configuration and getting the cookie values and encryption keys for the given
/// application.
///
public static void Initialize() {
if (_Initialized)
return;
lock(_lockObject) {
if (_Initialized)
return;
AuthenticationSection settings = RuntimeConfig.GetAppConfig().Authentication;
settings.ValidateAuthenticationMode();
_FormsName = settings.Forms.Name;
_RequireSSL = settings.Forms.RequireSSL;
_SlidingExpiration = settings.Forms.SlidingExpiration;
if (_FormsName == null)
_FormsName = CONFIG_DEFAULT_COOKIE;
_Protection = settings.Forms.Protection;
_Timeout = (int) settings.Forms.Timeout.TotalMinutes;
_FormsCookiePath = settings.Forms.Path;
_LoginUrl = settings.Forms.LoginUrl;
if (_LoginUrl == null)
_LoginUrl = "login.aspx";
_DefaultUrl = settings.Forms.DefaultUrl;
if (_DefaultUrl == null)
_DefaultUrl = "default.aspx";
_CookieMode = settings.Forms.Cookieless;
_CookieDomain = settings.Forms.Domain;
_EnableCrossAppRedirects = settings.Forms.EnableCrossAppRedirects;
_Initialized = true;
}
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Decrypt and get the auth ticket
///
/// Given an encrypted authenitcation ticket as
/// obtained from an HTTP cookie, this method returns an instance of a
/// FormsAuthenticationTicket class.
///
public static FormsAuthenticationTicket Decrypt(string encryptedTicket) {
if (String.IsNullOrEmpty(encryptedTicket) || encryptedTicket.Length > MAX_TICKET_LENGTH)
throw new ArgumentException(SR.GetString(SR.InvalidArgumentValue, "encryptedTicket"));
Initialize();
byte[] bBlob = null;
if ((encryptedTicket.Length % 2) == 0) { // Could be a hex string
try {
bBlob = MachineKeySection.HexStringToByteArray(encryptedTicket);
} catch { }
}
if (bBlob == null)
bBlob = HttpServerUtility.UrlTokenDecode(encryptedTicket);
if (bBlob == null || bBlob.Length < 1)
throw new ArgumentException(SR.GetString(SR.InvalidArgumentValue, "encryptedTicket"));
if (_Protection == FormsProtectionEnum.All || _Protection == FormsProtectionEnum.Encryption)
{
// DevDiv Bugs 137864: Include a random IV if under the right compat mode
// for improved encryption semantics
bBlob = MachineKeySection.EncryptOrDecryptData(false, bBlob, null, 0, bBlob.Length, IVType.Random);
if (bBlob == null)
return null;
}
int ticketLength = bBlob.Length;
if (_Protection == FormsProtectionEnum.All || _Protection == FormsProtectionEnum.Validation)
{
//////////////////////////////////////////////////////////////////////
// Step 2: Get the MAC: Last MAC_LENGTH bytes
if (bBlob.Length <= MAC_LENGTH)
return null;
ticketLength = ticketLength - MAC_LENGTH;
byte [] bMac = MachineKeySection.HashData(bBlob, null, 0, ticketLength);
//////////////////////////////////////////////////////////////////////
// Step 3: Make sure the MAC is correct
if (bMac == null) {
return null;
}
if (bMac.Length != MAC_LENGTH) {
return null;
}
for (int iter=0; iter MAX_TICKET_LENGTH) ? MAX_TICKET_LENGTH : ticketLength);
StringBuilder name = new StringBuilder(iSize);
StringBuilder data = new StringBuilder(iSize);
StringBuilder path = new StringBuilder(iSize);
byte [] pBin = new byte[2];
long [] pDates = new long[2];
// FEATURE_PAL: replace Forms Authentication ticket parsing
// functionality normally found in webengine.dll
#if !FEATURE_PAL
int iRet = UnsafeNativeMethods.CookieAuthParseTicket(bBlob, ticketLength,
name, iSize,
data, iSize,
path, iSize,
pBin, pDates);
#else // !FEATURE_PAL
int iRet = FormsAuthCoriolis.CookieAuthParseTicket(bBlob,
ticketLength, name, data, path, pBin, pDates);
#endif // !FEATURE_PAL
if (iRet != 0)
return null;
DateTime dt1 = DateTime.FromFileTime(pDates[0]);
DateTime dt2 = DateTime.FromFileTime(pDates[1]);
return new FormsAuthenticationTicket((int) pBin[0],
name.ToString(),
dt1,
dt2,
(bool) (pBin[1] != 0),
data.ToString(),
path.ToString());
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Encrypt a ticket
///
/// Given a FormsAuthenticationTicket, this
/// method produces a string containing an encrypted authentication ticket suitable
/// for use in an HTTP cookie.
///
public static String Encrypt(FormsAuthenticationTicket ticket) {
return Encrypt(ticket, true);
}
private static String Encrypt(FormsAuthenticationTicket ticket, bool hexEncodedTicket) {
if (ticket == null)
throw new ArgumentNullException("ticket");
Initialize();
//////////////////////////////////////////////////////////////////////
// Step 1: Make it into a binary blob
byte[] bBlob = MakeTicketIntoBinaryBlob(ticket);
if (bBlob == null)
return null;
//////////////////////////////////////////////////////////////////////
// Step 2: Get the MAC and add to the blob
if (_Protection == FormsProtectionEnum.All || _Protection == FormsProtectionEnum.Validation)
{
byte [] bMac = MachineKeySection.HashData(bBlob, null, 0, bBlob.Length);
if (bMac == null)
return null;
byte [] bAll = new byte[bMac.Length + bBlob.Length];
Buffer.BlockCopy(bBlob, 0, bAll, 0, bBlob.Length);
Buffer.BlockCopy(bMac, 0, bAll, bBlob.Length, bMac.Length);
bBlob = bAll;
}
if (_Protection == FormsProtectionEnum.All || _Protection == FormsProtectionEnum.Encryption) {
//////////////////////////////////////////////////////////////////////
// Step 3: Do the actual encryption
// DevDiv Bugs 137864: Include a random IV if under the right compat mode
// for improved encryption semantics
bBlob = MachineKeySection.EncryptOrDecryptData(true, bBlob, null, 0, bBlob.Length, IVType.Random);
}
if (!hexEncodedTicket)
return HttpServerUtility.UrlTokenEncode(bBlob);
else
return MachineKeySection.ByteArrayToHexString(bBlob, 0);
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Verify User name and Password
///
/// Given the supplied credentials, this method
/// attempts to validate the credentials against those contained in the configured
/// credential store.
///
public static bool Authenticate(String name, String password) {
bool retVal = InternalAuthenticate(name, password);
if (retVal) {
PerfCounters.IncrementCounter(AppPerfCounter.FORMS_AUTH_SUCCESS);
WebBaseEvent.RaiseSystemEvent(null, WebEventCodes.AuditFormsAuthenticationSuccess, name);
}
else {
PerfCounters.IncrementCounter(AppPerfCounter.FORMS_AUTH_FAIL);
WebBaseEvent.RaiseSystemEvent(null, WebEventCodes.AuditFormsAuthenticationFailure, name);
}
return retVal;
}
private static bool InternalAuthenticate(String name, String password) {
//////////////////////////////////////////////////////////////////////
// Step 1: Make sure we are initialized
if (name == null || password == null)
return false;
Initialize();
//////////////////////////////////////////////////////////////////////
// Step 2: Get the user database
AuthenticationSection settings = RuntimeConfig.GetAppConfig().Authentication;
settings.ValidateAuthenticationMode();
FormsAuthenticationUserCollection Users = settings.Forms.Credentials.Users;
// Hashtable hTable = settings.Credentials;
if (Users == null) {
return false;
}
//////////////////////////////////////////////////////////////////////
// Step 3: Get the (hashed) password for this user
FormsAuthenticationUser u = Users[name.ToLower(CultureInfo.InvariantCulture)];
if (u == null)
return false;
String pass = (String)u.Password;
if (pass == null) {
return false;
}
//////////////////////////////////////////////////////////////////////
// Step 4: Hash the given password
String encPassword;
switch (settings.Forms.Credentials.PasswordFormat)
{
case FormsAuthPasswordFormat.SHA1:
encPassword = HashPasswordForStoringInConfigFile(password, "sha1");
break;
case FormsAuthPasswordFormat.MD5:
encPassword = HashPasswordForStoringInConfigFile(password, "md5");
break;
case FormsAuthPasswordFormat.Clear:
encPassword = password;
break;
default:
return false;
}
//////////////////////////////////////////////////////////////////////
// Step 5: Compare the hashes
return(String.Compare(encPassword,
pass,
((settings.Forms.Credentials.PasswordFormat != FormsAuthPasswordFormat.Clear)
? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
== 0);
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
///
/// Given an authenticated user, calling SignOut
/// removes the authentication ticket by doing a SetForms with an empty value. This
/// removes either durable or session cookies.
///
public static void SignOut() {
Initialize();
HttpContext context = HttpContext.Current;
bool needToRedirect = context.CookielessHelper.DoesCookieValueExistInOriginal('F');
context.CookielessHelper.SetCookieValue('F', null); // Always clear the uri-cookie
if (!CookielessHelperClass.UseCookieless(context, false, CookieMode) || context.Request.Browser.Cookies)
{ // clear cookie if required
string cookieValue = String.Empty;
if (context.Request.Browser["supportsEmptyStringInCookieValue"] == "false")
cookieValue = "NoCookie";
HttpCookie cookie = new HttpCookie(FormsCookieName, cookieValue);
cookie.HttpOnly = true;
cookie.Path = _FormsCookiePath;
cookie.Expires = new System.DateTime(1999, 10, 12);
cookie.Secure = _RequireSSL;
if (_CookieDomain != null)
cookie.Domain = _CookieDomain;
context.Response.Cookies.RemoveCookie(FormsCookieName);
context.Response.Cookies.Add(cookie);
}
if (needToRedirect)
context.Response.Redirect(GetLoginPage(null), false);
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
///
/// This method creates an authentication ticket
/// for the given userName and attaches it to the cookies collection of the outgoing
/// response. It does not perform a redirect.
///
public static void SetAuthCookie(String userName, bool createPersistentCookie) {
Initialize();
SetAuthCookie(userName, createPersistentCookie, FormsAuthentication.FormsCookiePath);
}
///
/// This method creates an authentication ticket
/// for the given userName and attaches it to the cookies collection of the outgoing
/// response. It does not perform a redirect.
///
public static void SetAuthCookie(String userName, bool createPersistentCookie, String strCookiePath) {
Initialize();
HttpContext context = HttpContext.Current;
if (!context.Request.IsSecureConnection && RequireSSL)
throw new HttpException(SR.GetString(SR.Connection_not_secure_creating_secure_cookie));
bool cookieless = CookielessHelperClass.UseCookieless(context, false, CookieMode);
HttpCookie cookie = GetAuthCookie(userName, createPersistentCookie, cookieless ? "/" : strCookiePath, !cookieless);
if (!cookieless) {
HttpContext.Current.Response.Cookies.Add(cookie);
context.CookielessHelper.SetCookieValue('F', null);
}
else {
context.CookielessHelper.SetCookieValue('F', cookie.Value);
}
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
///
/// Creates an authentication cookie for a given
/// user name. This does not set the cookie as part of the outgoing response, so
/// that an application can have more control over how the cookie is issued.
///
public static HttpCookie GetAuthCookie(String userName, bool createPersistentCookie) {
Initialize();
return GetAuthCookie(userName, createPersistentCookie, FormsAuthentication.FormsCookiePath);
}
public static HttpCookie GetAuthCookie(String userName, bool createPersistentCookie, String strCookiePath) {
return GetAuthCookie(userName, createPersistentCookie, strCookiePath, true);
}
private static HttpCookie GetAuthCookie(String userName, bool createPersistentCookie, String strCookiePath, bool hexEncodedTicket) {
Initialize();
if (userName == null)
userName = String.Empty;
if (strCookiePath == null || strCookiePath.Length < 1)
strCookiePath = FormsCookiePath;
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
2, // version
userName, // User-Name
DateTime.Now, // Issue-Date
DateTime.Now.AddMinutes(_Timeout), // Expiration
createPersistentCookie, // IsPersistent
String.Empty, // User-Data
strCookiePath // Cookie Path
);
String strTicket = Encrypt(ticket, hexEncodedTicket);
if (strTicket == null || strTicket.Length < 1)
throw new HttpException(
SR.GetString(
SR.Unable_to_encrypt_cookie_ticket));
HttpCookie cookie = new HttpCookie(FormsCookieName, strTicket);
cookie.HttpOnly = true;
cookie.Path = strCookiePath;
cookie.Secure = _RequireSSL;
if (_CookieDomain != null)
cookie.Domain = _CookieDomain;
if (ticket.IsPersistent)
cookie.Expires = ticket.Expiration;
return cookie;
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
internal static String GetReturnUrl(bool useDefaultIfAbsent)
{
Initialize();
HttpContext context = HttpContext.Current;
String returnUrl = context.Request.QueryString[RETURN_URL];
// If it is not in the QueryString, look in the Posted-body
if (returnUrl == null) {
returnUrl = context.Request.Form[RETURN_URL];
if (!string.IsNullOrEmpty(returnUrl) && !returnUrl.Contains("/") && returnUrl.Contains("%"))
returnUrl = HttpUtility.UrlDecode(returnUrl);
}
// Make sure it is on the current server if EnableCrossAppRedirects is false
if (!string.IsNullOrEmpty(returnUrl) && !EnableCrossAppRedirects) {
if (!UrlPath.IsPathOnSameServer(returnUrl, context.Request.Url))
returnUrl = null;
}
// Make sure it is not dangerous, i.e. does not contain script, etc.
if (!string.IsNullOrEmpty(returnUrl) && CrossSiteScriptingValidation.IsDangerousUrl(returnUrl))
throw new HttpException(SR.GetString(SR.Invalid_redirect_return_url));
return ((returnUrl == null && useDefaultIfAbsent) ? DefaultUrl : returnUrl);
}
///
/// Returns the redirect URL for the original
/// request that caused the redirect to the login page.
///
public static String GetRedirectUrl(String userName, bool createPersistentCookie)
{
if (userName == null)
return null;
return GetReturnUrl(true);
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Redirect from logon page to orignal page
///
/// This method redirects an authenticated user
/// back to the original URL that they requested.
///
public static void RedirectFromLoginPage(String userName, bool createPersistentCookie) {
Initialize();
RedirectFromLoginPage(userName, createPersistentCookie, FormsAuthentication.FormsCookiePath);
}
public static void RedirectFromLoginPage(String userName, bool createPersistentCookie, String strCookiePath) {
Initialize();
if (userName == null)
return;
HttpContext context = HttpContext.Current;
string strUrl = GetReturnUrl(true);
if ( CookiesSupported || // Cookies-supported: Most common scenario
IsPathWithinAppRoot(context, strUrl)) { // Cookies not suported, so add it to the current app URL
SetAuthCookie(userName, createPersistentCookie, strCookiePath);
strUrl = RemoveQueryStringVariableFromUrl(strUrl, FormsCookieName); // Make sure there is no other ticket in the Query String.
if (!CookiesSupported) {// Make sure the URL is relative, if we are using cookieless.
int pos = strUrl.IndexOf("://", StringComparison.Ordinal);
if (pos > 0) {
pos = strUrl.IndexOf('/', pos + 3);
if (pos > 0)
strUrl = strUrl.Substring(pos);
}
}
} else if (EnableCrossAppRedirects) { // Cookieless scenario -- add it to the QueryString if allowed to
HttpCookie cookie = GetAuthCookie(userName, createPersistentCookie, strCookiePath);
strUrl = RemoveQueryStringVariableFromUrl(strUrl, cookie.Name); // Make sure there is no other ticket in the Query String.
if (strUrl.IndexOf('?') > 0) {
strUrl += "&" + cookie.Name + "=" + cookie.Value;
}
else {
strUrl += "?" + cookie.Name + "=" + cookie.Value;
}
} else {
// Broken scenario:
throw new HttpException(SR.GetString(SR.Can_not_issue_cookie_or_redirect));
}
context.Response.Redirect(strUrl, false);
}
public static FormsAuthenticationTicket RenewTicketIfOld(FormsAuthenticationTicket tOld) {
if (tOld == null)
return null;
DateTime dtN = DateTime.Now;
TimeSpan t1 = dtN - tOld.IssueDate;
TimeSpan t2 = tOld.Expiration - dtN;
if (t2 > t1)
return tOld;
return new FormsAuthenticationTicket (
tOld.Version,
tOld.Name,
dtN, // Issue Date: Now
dtN + (tOld.Expiration - tOld.IssueDate), // Expiration
tOld.IsPersistent,
tOld.UserData,
tOld.CookiePath);
}
public static String FormsCookieName { get { Initialize(); return _FormsName; }}
public static String FormsCookiePath { get { Initialize(); return _FormsCookiePath; }}
public static bool RequireSSL { get { Initialize(); return _RequireSSL; }}
public static bool SlidingExpiration { get { Initialize(); return _SlidingExpiration; }}
public static HttpCookieMode CookieMode { get { Initialize(); return _CookieMode; }}
public static string CookieDomain { get { Initialize ();return _CookieDomain; } }
public static bool EnableCrossAppRedirects { get { Initialize(); return _EnableCrossAppRedirects; } }
public static bool CookiesSupported {
get {
HttpContext context = HttpContext.Current;
if (context != null) {
return !(CookielessHelperClass.UseCookieless(context, false, CookieMode));
}
return true;
}
}
public static string LoginUrl {
get {
Initialize();
HttpContext context = HttpContext.Current;
if (context != null) {
return AuthenticationConfig.GetCompleteLoginUrl(context, _LoginUrl);
}
if (_LoginUrl.Length == 0 || (_LoginUrl[0] != '/' && _LoginUrl.IndexOf("//", StringComparison.Ordinal) < 0))
return ("/" + _LoginUrl);
return _LoginUrl;
}
}
public static string DefaultUrl {
get {
Initialize();
HttpContext context = HttpContext.Current;
if (context != null) {
return AuthenticationConfig.GetCompleteLoginUrl(context, _DefaultUrl);
}
if (_DefaultUrl.Length == 0 || (_DefaultUrl[0] != '/' && _DefaultUrl.IndexOf("//", StringComparison.Ordinal) < 0))
return ("/" + _DefaultUrl);
return _DefaultUrl;
}
}
internal static string GetLoginPage(string extraQueryString) {
return GetLoginPage(extraQueryString, false);
}
internal static string GetLoginPage(string extraQueryString, bool reuseReturnUrl) {
HttpContext context = HttpContext.Current;
string loginUrl = FormsAuthentication.LoginUrl;
if (loginUrl.IndexOf('?') >= 0)
loginUrl = RemoveQueryStringVariableFromUrl(loginUrl, RETURN_URL);
int pos = loginUrl.IndexOf('?');
if (pos < 0)
loginUrl += "?";
else
if (pos < loginUrl.Length -1)
loginUrl += "&";
string returnUrl = null;
if (reuseReturnUrl) {
returnUrl = HttpUtility.UrlEncode( GetReturnUrl(false),
context.Request.QueryStringEncoding );
}
if (returnUrl == null)
returnUrl = HttpUtility.UrlEncode(context.Request.PathWithQueryString, context.Request.ContentEncoding);
loginUrl += "ReturnUrl=" + returnUrl;
if (!String.IsNullOrEmpty(extraQueryString)) {
loginUrl += "&" + extraQueryString;
}
return loginUrl;
}
public static void RedirectToLoginPage() {
RedirectToLoginPage(null);
}
public static void RedirectToLoginPage(string extraQueryString) {
HttpContext context = HttpContext.Current;
string loginUrl = GetLoginPage(extraQueryString);
context.Response.Redirect(loginUrl, false);
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Private stuff
/////////////////////////////////////////////////////////////////////////////
// Config Tags
private const String CONFIG_DEFAULT_COOKIE = ".ASPXAUTH";
/////////////////////////////////////////////////////////////////////////////
// Private data
private static bool _Initialized;
private static String _FormsName;
//private static FormsProtectionEnum _Protection;
private static FormsProtectionEnum _Protection;
private static Int32 _Timeout;
private static String _FormsCookiePath;
private static bool _RequireSSL;
private static bool _SlidingExpiration;
private static string _LoginUrl;
private static string _DefaultUrl;
private static HttpCookieMode _CookieMode;
private static string _CookieDomain = null;
private static bool _EnableCrossAppRedirects;
/////////////////////////////////////////////////////////////////////////////
private static byte[] MakeTicketIntoBinaryBlob(FormsAuthenticationTicket ticket) {
byte [] bData = new byte[4096];
byte [] pBin = new byte[2];
long [] pDates = new long[2];
// DevDiv Bugs 137864: 8 bytes may not be enough random bits as the length should be equal to the
// key size. In CompatMode > Framework20SP1, use the IVType.Random feature instead of these 8 bytes,
// but still include empty 8 bytes for compat with webengine.dll, where CookieAuthConstructTicket is.
// Note that even in CompatMode = Framework20SP2 we fill 8 bytes with random data if the ticket
// is not going to be encrypted.
bool willEncrypt = (_Protection == FormsProtectionEnum.All || _Protection == FormsProtectionEnum.Encryption);
bool legacyPadding = !willEncrypt || (MachineKeySection.CompatMode == MachineKeyCompatibilityMode.Framework20SP1);
if (legacyPadding) {
// Fill the first 8 bytes of the blob with random bits
byte[] bRandom = new byte[8];
RNGCryptoServiceProvider randgen = new RNGCryptoServiceProvider();
randgen.GetBytes(bRandom);
Buffer.BlockCopy(bRandom, 0, bData, 0, 8);
}
else {
// use blank 8 bytes for compatibility with CookieAuthConstructTicket (do nothing)
}
pBin[0] = (byte) ticket.Version;
pBin[1] = (byte) (ticket.IsPersistent ? 1 : 0);
pDates[0] = ticket.IssueDate.ToFileTime();
pDates[1] = ticket.Expiration.ToFileTime();
// Coriolis: replace Forms Authentication ticket parsing
// functionality normally found in webengine.dll
#if !FEATURE_PAL
int iRet = UnsafeNativeMethods.CookieAuthConstructTicket(
bData, bData.Length,
ticket.Name, ticket.UserData, ticket.CookiePath,
pBin, pDates);
#else // !FEATURE_PAL
int iRet = FormsAuthCoriolis.CookieAuthConstructTicket(bData,
bData.Length, ticket.Name, ticket.UserData, ticket.CookiePath,
pBin, pDates);
#endif // !FEATURE_PAL
if (iRet < 0)
return null;
byte[] ciphertext = new byte[iRet];
Buffer.BlockCopy(bData, 0, ciphertext, 0, iRet);
return ciphertext;
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
internal static string RemoveQueryStringVariableFromUrl(string strUrl, string QSVar) {
int posQ = strUrl.IndexOf('?');
if (posQ < 0)
return strUrl;
// Remove non-encoded QSVars
string amp = @"&";
string question = @"?";
string token = amp + QSVar + "=";
RemoveQSVar(ref strUrl, posQ, token, amp, amp.Length);
token = question + QSVar + "=";
RemoveQSVar(ref strUrl, posQ, token, amp, question.Length);
// Remove Url-enocoded strings
amp = HttpUtility.UrlEncode(@"&");
question = HttpUtility.UrlEncode(@"?");
token = amp + HttpUtility.UrlEncode(QSVar + "=");
RemoveQSVar(ref strUrl, posQ, token, amp, amp.Length);
token = question + HttpUtility.UrlEncode(QSVar + "=");
RemoveQSVar(ref strUrl, posQ, token, amp, question.Length);
return strUrl;
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
static private void RemoveQSVar(ref string strUrl, int posQ, string token, string sep, int lenAtStartToLeave)
{
for (int pos = strUrl.LastIndexOf(token, StringComparison.Ordinal); pos >= posQ; pos = strUrl.LastIndexOf(token, StringComparison.Ordinal))
{
int end = strUrl.IndexOf(sep, pos + token.Length, StringComparison.Ordinal) + sep.Length;
if (end < sep.Length || end >= strUrl.Length)
{ // ending separator not found or nothing is at the end
strUrl = strUrl.Substring(0, pos);
}
else
{
strUrl = strUrl.Substring(0, pos + lenAtStartToLeave) + strUrl.Substring(end);
}
}
}
static private bool IsPathWithinAppRoot(HttpContext context, string path)
{
Uri absUri;
if (!Uri.TryCreate(path, UriKind.Absolute, out absUri))
return HttpRuntime.IsPathWithinAppRoot(path);
if (!absUri.IsLoopback && !string.Equals(context.Request.Url.Host, absUri.Host, StringComparison.OrdinalIgnoreCase))
return false; // different servers
return HttpRuntime.IsPathWithinAppRoot(absUri.AbsolutePath);
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//-----------------------------------------------------------------------------
/*
* FormsAuthentication class
*
* Copyright (c) 1999 Microsoft Corporation
*/
namespace System.Web.Security {
using System;
using System.Web;
using System.Text;
using System.Web.Configuration;
using System.Web.Caching;
using System.Collections;
using System.Web.Util;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Threading;
using System.Globalization;
using System.Security.Permissions;
using System.Web.Management;
///
/// This class consists of static methods that
/// provides helper utilities for manipulating authentication tickets.
///
[AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)]
public sealed class FormsAuthentication {
private const int MAC_LENGTH = 20;
private const int MAX_TICKET_LENGTH = 4096;
private static object _lockObject = new object();
internal const string RETURN_URL = "ReturnUrl";
public FormsAuthentication() { }
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Helper functions: Hash a password
///
/// Initializes FormsAuthentication by reading
/// configuration and getting the cookie values and encryption keys for the given
/// application.
///
public static String HashPasswordForStoringInConfigFile(String password, String passwordFormat) {
if (password == null) {
throw new ArgumentNullException("password");
}
if (passwordFormat == null) {
throw new ArgumentNullException("passwordFormat");
}
HashAlgorithm hashAlgorithm;
if (StringUtil.EqualsIgnoreCase(passwordFormat, "sha1"))
hashAlgorithm = SHA1.Create();
else if (StringUtil.EqualsIgnoreCase(passwordFormat, "md5"))
hashAlgorithm = MD5.Create();
else
throw new ArgumentException(SR.GetString(SR.InvalidArgumentValue, "passwordFormat"));
return MachineKeySection.ByteArrayToHexString(hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(password)), 0);
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Initialize this
///
/// Initializes FormsAuthentication by reading
/// configuration and getting the cookie values and encryption keys for the given
/// application.
///
public static void Initialize() {
if (_Initialized)
return;
lock(_lockObject) {
if (_Initialized)
return;
AuthenticationSection settings = RuntimeConfig.GetAppConfig().Authentication;
settings.ValidateAuthenticationMode();
_FormsName = settings.Forms.Name;
_RequireSSL = settings.Forms.RequireSSL;
_SlidingExpiration = settings.Forms.SlidingExpiration;
if (_FormsName == null)
_FormsName = CONFIG_DEFAULT_COOKIE;
_Protection = settings.Forms.Protection;
_Timeout = (int) settings.Forms.Timeout.TotalMinutes;
_FormsCookiePath = settings.Forms.Path;
_LoginUrl = settings.Forms.LoginUrl;
if (_LoginUrl == null)
_LoginUrl = "login.aspx";
_DefaultUrl = settings.Forms.DefaultUrl;
if (_DefaultUrl == null)
_DefaultUrl = "default.aspx";
_CookieMode = settings.Forms.Cookieless;
_CookieDomain = settings.Forms.Domain;
_EnableCrossAppRedirects = settings.Forms.EnableCrossAppRedirects;
_Initialized = true;
}
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Decrypt and get the auth ticket
///
/// Given an encrypted authenitcation ticket as
/// obtained from an HTTP cookie, this method returns an instance of a
/// FormsAuthenticationTicket class.
///
public static FormsAuthenticationTicket Decrypt(string encryptedTicket) {
if (String.IsNullOrEmpty(encryptedTicket) || encryptedTicket.Length > MAX_TICKET_LENGTH)
throw new ArgumentException(SR.GetString(SR.InvalidArgumentValue, "encryptedTicket"));
Initialize();
byte[] bBlob = null;
if ((encryptedTicket.Length % 2) == 0) { // Could be a hex string
try {
bBlob = MachineKeySection.HexStringToByteArray(encryptedTicket);
} catch { }
}
if (bBlob == null)
bBlob = HttpServerUtility.UrlTokenDecode(encryptedTicket);
if (bBlob == null || bBlob.Length < 1)
throw new ArgumentException(SR.GetString(SR.InvalidArgumentValue, "encryptedTicket"));
if (_Protection == FormsProtectionEnum.All || _Protection == FormsProtectionEnum.Encryption)
{
// DevDiv Bugs 137864: Include a random IV if under the right compat mode
// for improved encryption semantics
bBlob = MachineKeySection.EncryptOrDecryptData(false, bBlob, null, 0, bBlob.Length, IVType.Random);
if (bBlob == null)
return null;
}
int ticketLength = bBlob.Length;
if (_Protection == FormsProtectionEnum.All || _Protection == FormsProtectionEnum.Validation)
{
//////////////////////////////////////////////////////////////////////
// Step 2: Get the MAC: Last MAC_LENGTH bytes
if (bBlob.Length <= MAC_LENGTH)
return null;
ticketLength = ticketLength - MAC_LENGTH;
byte [] bMac = MachineKeySection.HashData(bBlob, null, 0, ticketLength);
//////////////////////////////////////////////////////////////////////
// Step 3: Make sure the MAC is correct
if (bMac == null) {
return null;
}
if (bMac.Length != MAC_LENGTH) {
return null;
}
for (int iter=0; iter MAX_TICKET_LENGTH) ? MAX_TICKET_LENGTH : ticketLength);
StringBuilder name = new StringBuilder(iSize);
StringBuilder data = new StringBuilder(iSize);
StringBuilder path = new StringBuilder(iSize);
byte [] pBin = new byte[2];
long [] pDates = new long[2];
// FEATURE_PAL: replace Forms Authentication ticket parsing
// functionality normally found in webengine.dll
#if !FEATURE_PAL
int iRet = UnsafeNativeMethods.CookieAuthParseTicket(bBlob, ticketLength,
name, iSize,
data, iSize,
path, iSize,
pBin, pDates);
#else // !FEATURE_PAL
int iRet = FormsAuthCoriolis.CookieAuthParseTicket(bBlob,
ticketLength, name, data, path, pBin, pDates);
#endif // !FEATURE_PAL
if (iRet != 0)
return null;
DateTime dt1 = DateTime.FromFileTime(pDates[0]);
DateTime dt2 = DateTime.FromFileTime(pDates[1]);
return new FormsAuthenticationTicket((int) pBin[0],
name.ToString(),
dt1,
dt2,
(bool) (pBin[1] != 0),
data.ToString(),
path.ToString());
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Encrypt a ticket
///
/// Given a FormsAuthenticationTicket, this
/// method produces a string containing an encrypted authentication ticket suitable
/// for use in an HTTP cookie.
///
public static String Encrypt(FormsAuthenticationTicket ticket) {
return Encrypt(ticket, true);
}
private static String Encrypt(FormsAuthenticationTicket ticket, bool hexEncodedTicket) {
if (ticket == null)
throw new ArgumentNullException("ticket");
Initialize();
//////////////////////////////////////////////////////////////////////
// Step 1: Make it into a binary blob
byte[] bBlob = MakeTicketIntoBinaryBlob(ticket);
if (bBlob == null)
return null;
//////////////////////////////////////////////////////////////////////
// Step 2: Get the MAC and add to the blob
if (_Protection == FormsProtectionEnum.All || _Protection == FormsProtectionEnum.Validation)
{
byte [] bMac = MachineKeySection.HashData(bBlob, null, 0, bBlob.Length);
if (bMac == null)
return null;
byte [] bAll = new byte[bMac.Length + bBlob.Length];
Buffer.BlockCopy(bBlob, 0, bAll, 0, bBlob.Length);
Buffer.BlockCopy(bMac, 0, bAll, bBlob.Length, bMac.Length);
bBlob = bAll;
}
if (_Protection == FormsProtectionEnum.All || _Protection == FormsProtectionEnum.Encryption) {
//////////////////////////////////////////////////////////////////////
// Step 3: Do the actual encryption
// DevDiv Bugs 137864: Include a random IV if under the right compat mode
// for improved encryption semantics
bBlob = MachineKeySection.EncryptOrDecryptData(true, bBlob, null, 0, bBlob.Length, IVType.Random);
}
if (!hexEncodedTicket)
return HttpServerUtility.UrlTokenEncode(bBlob);
else
return MachineKeySection.ByteArrayToHexString(bBlob, 0);
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Verify User name and Password
///
/// Given the supplied credentials, this method
/// attempts to validate the credentials against those contained in the configured
/// credential store.
///
public static bool Authenticate(String name, String password) {
bool retVal = InternalAuthenticate(name, password);
if (retVal) {
PerfCounters.IncrementCounter(AppPerfCounter.FORMS_AUTH_SUCCESS);
WebBaseEvent.RaiseSystemEvent(null, WebEventCodes.AuditFormsAuthenticationSuccess, name);
}
else {
PerfCounters.IncrementCounter(AppPerfCounter.FORMS_AUTH_FAIL);
WebBaseEvent.RaiseSystemEvent(null, WebEventCodes.AuditFormsAuthenticationFailure, name);
}
return retVal;
}
private static bool InternalAuthenticate(String name, String password) {
//////////////////////////////////////////////////////////////////////
// Step 1: Make sure we are initialized
if (name == null || password == null)
return false;
Initialize();
//////////////////////////////////////////////////////////////////////
// Step 2: Get the user database
AuthenticationSection settings = RuntimeConfig.GetAppConfig().Authentication;
settings.ValidateAuthenticationMode();
FormsAuthenticationUserCollection Users = settings.Forms.Credentials.Users;
// Hashtable hTable = settings.Credentials;
if (Users == null) {
return false;
}
//////////////////////////////////////////////////////////////////////
// Step 3: Get the (hashed) password for this user
FormsAuthenticationUser u = Users[name.ToLower(CultureInfo.InvariantCulture)];
if (u == null)
return false;
String pass = (String)u.Password;
if (pass == null) {
return false;
}
//////////////////////////////////////////////////////////////////////
// Step 4: Hash the given password
String encPassword;
switch (settings.Forms.Credentials.PasswordFormat)
{
case FormsAuthPasswordFormat.SHA1:
encPassword = HashPasswordForStoringInConfigFile(password, "sha1");
break;
case FormsAuthPasswordFormat.MD5:
encPassword = HashPasswordForStoringInConfigFile(password, "md5");
break;
case FormsAuthPasswordFormat.Clear:
encPassword = password;
break;
default:
return false;
}
//////////////////////////////////////////////////////////////////////
// Step 5: Compare the hashes
return(String.Compare(encPassword,
pass,
((settings.Forms.Credentials.PasswordFormat != FormsAuthPasswordFormat.Clear)
? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
== 0);
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
///
/// Given an authenticated user, calling SignOut
/// removes the authentication ticket by doing a SetForms with an empty value. This
/// removes either durable or session cookies.
///
public static void SignOut() {
Initialize();
HttpContext context = HttpContext.Current;
bool needToRedirect = context.CookielessHelper.DoesCookieValueExistInOriginal('F');
context.CookielessHelper.SetCookieValue('F', null); // Always clear the uri-cookie
if (!CookielessHelperClass.UseCookieless(context, false, CookieMode) || context.Request.Browser.Cookies)
{ // clear cookie if required
string cookieValue = String.Empty;
if (context.Request.Browser["supportsEmptyStringInCookieValue"] == "false")
cookieValue = "NoCookie";
HttpCookie cookie = new HttpCookie(FormsCookieName, cookieValue);
cookie.HttpOnly = true;
cookie.Path = _FormsCookiePath;
cookie.Expires = new System.DateTime(1999, 10, 12);
cookie.Secure = _RequireSSL;
if (_CookieDomain != null)
cookie.Domain = _CookieDomain;
context.Response.Cookies.RemoveCookie(FormsCookieName);
context.Response.Cookies.Add(cookie);
}
if (needToRedirect)
context.Response.Redirect(GetLoginPage(null), false);
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
///
/// This method creates an authentication ticket
/// for the given userName and attaches it to the cookies collection of the outgoing
/// response. It does not perform a redirect.
///
public static void SetAuthCookie(String userName, bool createPersistentCookie) {
Initialize();
SetAuthCookie(userName, createPersistentCookie, FormsAuthentication.FormsCookiePath);
}
///
/// This method creates an authentication ticket
/// for the given userName and attaches it to the cookies collection of the outgoing
/// response. It does not perform a redirect.
///
public static void SetAuthCookie(String userName, bool createPersistentCookie, String strCookiePath) {
Initialize();
HttpContext context = HttpContext.Current;
if (!context.Request.IsSecureConnection && RequireSSL)
throw new HttpException(SR.GetString(SR.Connection_not_secure_creating_secure_cookie));
bool cookieless = CookielessHelperClass.UseCookieless(context, false, CookieMode);
HttpCookie cookie = GetAuthCookie(userName, createPersistentCookie, cookieless ? "/" : strCookiePath, !cookieless);
if (!cookieless) {
HttpContext.Current.Response.Cookies.Add(cookie);
context.CookielessHelper.SetCookieValue('F', null);
}
else {
context.CookielessHelper.SetCookieValue('F', cookie.Value);
}
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
///
/// Creates an authentication cookie for a given
/// user name. This does not set the cookie as part of the outgoing response, so
/// that an application can have more control over how the cookie is issued.
///
public static HttpCookie GetAuthCookie(String userName, bool createPersistentCookie) {
Initialize();
return GetAuthCookie(userName, createPersistentCookie, FormsAuthentication.FormsCookiePath);
}
public static HttpCookie GetAuthCookie(String userName, bool createPersistentCookie, String strCookiePath) {
return GetAuthCookie(userName, createPersistentCookie, strCookiePath, true);
}
private static HttpCookie GetAuthCookie(String userName, bool createPersistentCookie, String strCookiePath, bool hexEncodedTicket) {
Initialize();
if (userName == null)
userName = String.Empty;
if (strCookiePath == null || strCookiePath.Length < 1)
strCookiePath = FormsCookiePath;
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
2, // version
userName, // User-Name
DateTime.Now, // Issue-Date
DateTime.Now.AddMinutes(_Timeout), // Expiration
createPersistentCookie, // IsPersistent
String.Empty, // User-Data
strCookiePath // Cookie Path
);
String strTicket = Encrypt(ticket, hexEncodedTicket);
if (strTicket == null || strTicket.Length < 1)
throw new HttpException(
SR.GetString(
SR.Unable_to_encrypt_cookie_ticket));
HttpCookie cookie = new HttpCookie(FormsCookieName, strTicket);
cookie.HttpOnly = true;
cookie.Path = strCookiePath;
cookie.Secure = _RequireSSL;
if (_CookieDomain != null)
cookie.Domain = _CookieDomain;
if (ticket.IsPersistent)
cookie.Expires = ticket.Expiration;
return cookie;
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
internal static String GetReturnUrl(bool useDefaultIfAbsent)
{
Initialize();
HttpContext context = HttpContext.Current;
String returnUrl = context.Request.QueryString[RETURN_URL];
// If it is not in the QueryString, look in the Posted-body
if (returnUrl == null) {
returnUrl = context.Request.Form[RETURN_URL];
if (!string.IsNullOrEmpty(returnUrl) && !returnUrl.Contains("/") && returnUrl.Contains("%"))
returnUrl = HttpUtility.UrlDecode(returnUrl);
}
// Make sure it is on the current server if EnableCrossAppRedirects is false
if (!string.IsNullOrEmpty(returnUrl) && !EnableCrossAppRedirects) {
if (!UrlPath.IsPathOnSameServer(returnUrl, context.Request.Url))
returnUrl = null;
}
// Make sure it is not dangerous, i.e. does not contain script, etc.
if (!string.IsNullOrEmpty(returnUrl) && CrossSiteScriptingValidation.IsDangerousUrl(returnUrl))
throw new HttpException(SR.GetString(SR.Invalid_redirect_return_url));
return ((returnUrl == null && useDefaultIfAbsent) ? DefaultUrl : returnUrl);
}
///
/// Returns the redirect URL for the original
/// request that caused the redirect to the login page.
///
public static String GetRedirectUrl(String userName, bool createPersistentCookie)
{
if (userName == null)
return null;
return GetReturnUrl(true);
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Redirect from logon page to orignal page
///
/// This method redirects an authenticated user
/// back to the original URL that they requested.
///
public static void RedirectFromLoginPage(String userName, bool createPersistentCookie) {
Initialize();
RedirectFromLoginPage(userName, createPersistentCookie, FormsAuthentication.FormsCookiePath);
}
public static void RedirectFromLoginPage(String userName, bool createPersistentCookie, String strCookiePath) {
Initialize();
if (userName == null)
return;
HttpContext context = HttpContext.Current;
string strUrl = GetReturnUrl(true);
if ( CookiesSupported || // Cookies-supported: Most common scenario
IsPathWithinAppRoot(context, strUrl)) { // Cookies not suported, so add it to the current app URL
SetAuthCookie(userName, createPersistentCookie, strCookiePath);
strUrl = RemoveQueryStringVariableFromUrl(strUrl, FormsCookieName); // Make sure there is no other ticket in the Query String.
if (!CookiesSupported) {// Make sure the URL is relative, if we are using cookieless.
int pos = strUrl.IndexOf("://", StringComparison.Ordinal);
if (pos > 0) {
pos = strUrl.IndexOf('/', pos + 3);
if (pos > 0)
strUrl = strUrl.Substring(pos);
}
}
} else if (EnableCrossAppRedirects) { // Cookieless scenario -- add it to the QueryString if allowed to
HttpCookie cookie = GetAuthCookie(userName, createPersistentCookie, strCookiePath);
strUrl = RemoveQueryStringVariableFromUrl(strUrl, cookie.Name); // Make sure there is no other ticket in the Query String.
if (strUrl.IndexOf('?') > 0) {
strUrl += "&" + cookie.Name + "=" + cookie.Value;
}
else {
strUrl += "?" + cookie.Name + "=" + cookie.Value;
}
} else {
// Broken scenario:
throw new HttpException(SR.GetString(SR.Can_not_issue_cookie_or_redirect));
}
context.Response.Redirect(strUrl, false);
}
public static FormsAuthenticationTicket RenewTicketIfOld(FormsAuthenticationTicket tOld) {
if (tOld == null)
return null;
DateTime dtN = DateTime.Now;
TimeSpan t1 = dtN - tOld.IssueDate;
TimeSpan t2 = tOld.Expiration - dtN;
if (t2 > t1)
return tOld;
return new FormsAuthenticationTicket (
tOld.Version,
tOld.Name,
dtN, // Issue Date: Now
dtN + (tOld.Expiration - tOld.IssueDate), // Expiration
tOld.IsPersistent,
tOld.UserData,
tOld.CookiePath);
}
public static String FormsCookieName { get { Initialize(); return _FormsName; }}
public static String FormsCookiePath { get { Initialize(); return _FormsCookiePath; }}
public static bool RequireSSL { get { Initialize(); return _RequireSSL; }}
public static bool SlidingExpiration { get { Initialize(); return _SlidingExpiration; }}
public static HttpCookieMode CookieMode { get { Initialize(); return _CookieMode; }}
public static string CookieDomain { get { Initialize ();return _CookieDomain; } }
public static bool EnableCrossAppRedirects { get { Initialize(); return _EnableCrossAppRedirects; } }
public static bool CookiesSupported {
get {
HttpContext context = HttpContext.Current;
if (context != null) {
return !(CookielessHelperClass.UseCookieless(context, false, CookieMode));
}
return true;
}
}
public static string LoginUrl {
get {
Initialize();
HttpContext context = HttpContext.Current;
if (context != null) {
return AuthenticationConfig.GetCompleteLoginUrl(context, _LoginUrl);
}
if (_LoginUrl.Length == 0 || (_LoginUrl[0] != '/' && _LoginUrl.IndexOf("//", StringComparison.Ordinal) < 0))
return ("/" + _LoginUrl);
return _LoginUrl;
}
}
public static string DefaultUrl {
get {
Initialize();
HttpContext context = HttpContext.Current;
if (context != null) {
return AuthenticationConfig.GetCompleteLoginUrl(context, _DefaultUrl);
}
if (_DefaultUrl.Length == 0 || (_DefaultUrl[0] != '/' && _DefaultUrl.IndexOf("//", StringComparison.Ordinal) < 0))
return ("/" + _DefaultUrl);
return _DefaultUrl;
}
}
internal static string GetLoginPage(string extraQueryString) {
return GetLoginPage(extraQueryString, false);
}
internal static string GetLoginPage(string extraQueryString, bool reuseReturnUrl) {
HttpContext context = HttpContext.Current;
string loginUrl = FormsAuthentication.LoginUrl;
if (loginUrl.IndexOf('?') >= 0)
loginUrl = RemoveQueryStringVariableFromUrl(loginUrl, RETURN_URL);
int pos = loginUrl.IndexOf('?');
if (pos < 0)
loginUrl += "?";
else
if (pos < loginUrl.Length -1)
loginUrl += "&";
string returnUrl = null;
if (reuseReturnUrl) {
returnUrl = HttpUtility.UrlEncode( GetReturnUrl(false),
context.Request.QueryStringEncoding );
}
if (returnUrl == null)
returnUrl = HttpUtility.UrlEncode(context.Request.PathWithQueryString, context.Request.ContentEncoding);
loginUrl += "ReturnUrl=" + returnUrl;
if (!String.IsNullOrEmpty(extraQueryString)) {
loginUrl += "&" + extraQueryString;
}
return loginUrl;
}
public static void RedirectToLoginPage() {
RedirectToLoginPage(null);
}
public static void RedirectToLoginPage(string extraQueryString) {
HttpContext context = HttpContext.Current;
string loginUrl = GetLoginPage(extraQueryString);
context.Response.Redirect(loginUrl, false);
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Private stuff
/////////////////////////////////////////////////////////////////////////////
// Config Tags
private const String CONFIG_DEFAULT_COOKIE = ".ASPXAUTH";
/////////////////////////////////////////////////////////////////////////////
// Private data
private static bool _Initialized;
private static String _FormsName;
//private static FormsProtectionEnum _Protection;
private static FormsProtectionEnum _Protection;
private static Int32 _Timeout;
private static String _FormsCookiePath;
private static bool _RequireSSL;
private static bool _SlidingExpiration;
private static string _LoginUrl;
private static string _DefaultUrl;
private static HttpCookieMode _CookieMode;
private static string _CookieDomain = null;
private static bool _EnableCrossAppRedirects;
/////////////////////////////////////////////////////////////////////////////
private static byte[] MakeTicketIntoBinaryBlob(FormsAuthenticationTicket ticket) {
byte [] bData = new byte[4096];
byte [] pBin = new byte[2];
long [] pDates = new long[2];
// DevDiv Bugs 137864: 8 bytes may not be enough random bits as the length should be equal to the
// key size. In CompatMode > Framework20SP1, use the IVType.Random feature instead of these 8 bytes,
// but still include empty 8 bytes for compat with webengine.dll, where CookieAuthConstructTicket is.
// Note that even in CompatMode = Framework20SP2 we fill 8 bytes with random data if the ticket
// is not going to be encrypted.
bool willEncrypt = (_Protection == FormsProtectionEnum.All || _Protection == FormsProtectionEnum.Encryption);
bool legacyPadding = !willEncrypt || (MachineKeySection.CompatMode == MachineKeyCompatibilityMode.Framework20SP1);
if (legacyPadding) {
// Fill the first 8 bytes of the blob with random bits
byte[] bRandom = new byte[8];
RNGCryptoServiceProvider randgen = new RNGCryptoServiceProvider();
randgen.GetBytes(bRandom);
Buffer.BlockCopy(bRandom, 0, bData, 0, 8);
}
else {
// use blank 8 bytes for compatibility with CookieAuthConstructTicket (do nothing)
}
pBin[0] = (byte) ticket.Version;
pBin[1] = (byte) (ticket.IsPersistent ? 1 : 0);
pDates[0] = ticket.IssueDate.ToFileTime();
pDates[1] = ticket.Expiration.ToFileTime();
// Coriolis: replace Forms Authentication ticket parsing
// functionality normally found in webengine.dll
#if !FEATURE_PAL
int iRet = UnsafeNativeMethods.CookieAuthConstructTicket(
bData, bData.Length,
ticket.Name, ticket.UserData, ticket.CookiePath,
pBin, pDates);
#else // !FEATURE_PAL
int iRet = FormsAuthCoriolis.CookieAuthConstructTicket(bData,
bData.Length, ticket.Name, ticket.UserData, ticket.CookiePath,
pBin, pDates);
#endif // !FEATURE_PAL
if (iRet < 0)
return null;
byte[] ciphertext = new byte[iRet];
Buffer.BlockCopy(bData, 0, ciphertext, 0, iRet);
return ciphertext;
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
internal static string RemoveQueryStringVariableFromUrl(string strUrl, string QSVar) {
int posQ = strUrl.IndexOf('?');
if (posQ < 0)
return strUrl;
// Remove non-encoded QSVars
string amp = @"&";
string question = @"?";
string token = amp + QSVar + "=";
RemoveQSVar(ref strUrl, posQ, token, amp, amp.Length);
token = question + QSVar + "=";
RemoveQSVar(ref strUrl, posQ, token, amp, question.Length);
// Remove Url-enocoded strings
amp = HttpUtility.UrlEncode(@"&");
question = HttpUtility.UrlEncode(@"?");
token = amp + HttpUtility.UrlEncode(QSVar + "=");
RemoveQSVar(ref strUrl, posQ, token, amp, amp.Length);
token = question + HttpUtility.UrlEncode(QSVar + "=");
RemoveQSVar(ref strUrl, posQ, token, amp, question.Length);
return strUrl;
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
static private void RemoveQSVar(ref string strUrl, int posQ, string token, string sep, int lenAtStartToLeave)
{
for (int pos = strUrl.LastIndexOf(token, StringComparison.Ordinal); pos >= posQ; pos = strUrl.LastIndexOf(token, StringComparison.Ordinal))
{
int end = strUrl.IndexOf(sep, pos + token.Length, StringComparison.Ordinal) + sep.Length;
if (end < sep.Length || end >= strUrl.Length)
{ // ending separator not found or nothing is at the end
strUrl = strUrl.Substring(0, pos);
}
else
{
strUrl = strUrl.Substring(0, pos + lenAtStartToLeave) + strUrl.Substring(end);
}
}
}
static private bool IsPathWithinAppRoot(HttpContext context, string path)
{
Uri absUri;
if (!Uri.TryCreate(path, UriKind.Absolute, out absUri))
return HttpRuntime.IsPathWithinAppRoot(path);
if (!absUri.IsLoopback && !string.Equals(context.Request.Url.Host, absUri.Host, StringComparison.OrdinalIgnoreCase))
return false; // different servers
return HttpRuntime.IsPathWithinAppRoot(absUri.AbsolutePath);
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ProvidersHelper.cs
- CodeStatement.cs
- CuspData.cs
- ManagedWndProcTracker.cs
- NamespaceTable.cs
- ToolBarOverflowPanel.cs
- HostSecurityManager.cs
- AttributeEmitter.cs
- NameValuePermission.cs
- StorageComplexTypeMapping.cs
- XmlnsCompatibleWithAttribute.cs
- WebPartCancelEventArgs.cs
- OrderingQueryOperator.cs
- EventPrivateKey.cs
- DefaultValueConverter.cs
- TrackingServices.cs
- Timer.cs
- CompiledRegexRunnerFactory.cs
- Types.cs
- DynamicRenderer.cs
- OdbcConnection.cs
- SqlDesignerDataSourceView.cs
- ClearTypeHintValidation.cs
- ICollection.cs
- TransformationRules.cs
- bidPrivateBase.cs
- XmlILConstructAnalyzer.cs
- PhoneCallDesigner.cs
- WorkflowServiceBehavior.cs
- XmlSerializationWriter.cs
- KeyInstance.cs
- ClearTypeHintValidation.cs
- GroupBoxAutomationPeer.cs
- ClientApiGenerator.cs
- SigningCredentials.cs
- XmlHierarchyData.cs
- InvariantComparer.cs
- XmlName.cs
- SchemaType.cs
- ProtocolsConfigurationEntry.cs
- EntityReference.cs
- DataPointer.cs
- CodeSnippetExpression.cs
- AttributeEmitter.cs
- FieldMetadata.cs
- Funcletizer.cs
- AtomEntry.cs
- SmtpNetworkElement.cs
- Message.cs
- ContractNamespaceAttribute.cs
- IdentityValidationException.cs
- Variable.cs
- FileUtil.cs
- ManualResetEvent.cs
- Type.cs
- HttpRuntimeSection.cs
- TripleDES.cs
- MediaPlayerState.cs
- ImmutableAssemblyCacheEntry.cs
- ExpressionReplacer.cs
- HyperLinkDataBindingHandler.cs
- WindowsRegion.cs
- IntSecurity.cs
- WindowsGraphicsWrapper.cs
- ExtensionDataReader.cs
- TagPrefixAttribute.cs
- AuthorizationSection.cs
- ProgressiveCrcCalculatingStream.cs
- MatrixConverter.cs
- LocalizationParserHooks.cs
- BuildManagerHost.cs
- SimpleColumnProvider.cs
- PropertyCollection.cs
- MultiView.cs
- FrameworkTextComposition.cs
- WindowAutomationPeer.cs
- UtilityExtension.cs
- TypedTableBaseExtensions.cs
- WebResponse.cs
- Publisher.cs
- uribuilder.cs
- DeviceContexts.cs
- remotingproxy.cs
- externdll.cs
- RuntimeWrappedException.cs
- ImmutableObjectAttribute.cs
- AutoGeneratedField.cs
- FixedSOMPageConstructor.cs
- CreateUserWizard.cs
- Convert.cs
- BamlStream.cs
- DecoratedNameAttribute.cs
- CodeMethodInvokeExpression.cs
- ClaimComparer.cs
- ValidatorUtils.cs
- ChildrenQuery.cs
- StringUtil.cs
- ToolStripControlHost.cs
- ValueExpressions.cs
- FormViewDeleteEventArgs.cs