// Gonzalo Paniagua Javier (gonzalo@ximian.com)
//
// (C) 2002,2003 Ximian, Inc (http://www.ximian.com)
-// Copyright (c) 2005 Novell, Inc (http://www.novell.com)
+// Copyright (c) 2005-2010 Novell, Inc (http://www.novell.com)
//
//
using System.Text;
using System.Web;
using System.Web.Configuration;
+using System.Web.Compilation;
using System.Web.Util;
using System.Globalization;
+using System.Collections.Specialized;
namespace System.Web.Security
{
[AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
public sealed class FormsAuthentication
{
- const int MD5_hash_size = 16;
- const int SHA1_hash_size = 20;
-
static string authConfigPath = "system.web/authentication";
static string machineKeyConfigPath = "system.web/machineKey";
-#if TARGET_J2EE
- const string Forms_initialized = "Forms.initialized";
- const string Forms_cookieName = "Forms.cookieName";
- const string Forms_cookiePath = "Forms.cookiePath";
- const string Forms_timeout = "Forms.timeout";
- const string Forms_protection = "Forms.protection";
- const string Forms_init_vector = "Forms.init_vector";
- static bool initialized
- {
- get {
- object o = AppDomain.CurrentDomain.GetData (Forms_initialized);
- return o != null ? (bool) o : false;
- }
- set { AppDomain.CurrentDomain.SetData (Forms_initialized, value); }
- }
- static string cookieName
- {
- get { return (string) AppDomain.CurrentDomain.GetData (Forms_cookieName); }
- set { AppDomain.CurrentDomain.SetData (Forms_cookieName, value); }
- }
- static string cookiePath
- {
- get { return (string) AppDomain.CurrentDomain.GetData (Forms_cookiePath); }
- set { AppDomain.CurrentDomain.SetData (Forms_cookiePath, value); }
- }
- static int timeout
- {
- get {
- object o = AppDomain.CurrentDomain.GetData (Forms_timeout);
- return o != null ? (int) o : 0;
- }
- set { AppDomain.CurrentDomain.SetData (Forms_timeout, value); }
- }
- static FormsProtectionEnum protection
- {
- get { return (FormsProtectionEnum) AppDomain.CurrentDomain.GetData (Forms_protection); }
- set { AppDomain.CurrentDomain.SetData (Forms_protection, value); }
- }
- static byte [] init_vector
- {
- get { return (byte []) AppDomain.CurrentDomain.GetData (Forms_init_vector); }
- set { AppDomain.CurrentDomain.SetData (Forms_init_vector, value); }
- }
static object locker = new object ();
-#else
static bool initialized;
static string cookieName;
static string cookiePath;
static int timeout;
static FormsProtectionEnum protection;
- static object locker = new object ();
- static byte [] init_vector; // initialization vector used for 3DES
-#endif
-#if NET_1_1
-#if TARGET_J2EE
- const string Forms_requireSSL = "Forms.requireSSL";
- const string Forms_slidingExpiration = "Forms.slidingExpiration";
-
- static bool requireSSL
- {
- get {
- object o = AppDomain.CurrentDomain.GetData (Forms_requireSSL);
- return o != null ? (bool) o : false;
- }
- set { AppDomain.CurrentDomain.SetData (Forms_requireSSL, value); }
- }
- static bool slidingExpiration
- {
- get {
- object o = AppDomain.CurrentDomain.GetData (Forms_slidingExpiration);
- return o != null ? (bool) o : false;
- }
- set { AppDomain.CurrentDomain.SetData (Forms_slidingExpiration, value); }
- }
-#else
static bool requireSSL;
static bool slidingExpiration;
-#endif
-#endif
-#if NET_2_0
-#if TARGET_J2EE
- const string Forms_cookie_domain = "Forms.cookie_domain";
- const string Forms_cookie_mode = "Forms.cookie_mode";
- const string Forms_cookies_supported = "Forms.cookies_supported";
- const string Forms_default_url = "Forms.default_url";
- const string Forms_enable_crossapp_redirects = "Forms.enable_crossapp_redirects";
- const string Forms_login_url = "Forms.login_url";
- static string cookie_domain
- {
- get { return (string) AppDomain.CurrentDomain.GetData (Forms_cookie_domain); }
- set { AppDomain.CurrentDomain.SetData (Forms_cookie_domain, value); }
- }
- static HttpCookieMode cookie_mode
- {
- get { return (HttpCookieMode) AppDomain.CurrentDomain.GetData (Forms_cookie_mode); }
- set { AppDomain.CurrentDomain.SetData (Forms_cookie_mode, value); }
- }
- static bool cookies_supported
- {
- get {
- object o = AppDomain.CurrentDomain.GetData (Forms_cookies_supported);
- return o != null ? (bool) o : false;
- }
- set { AppDomain.CurrentDomain.SetData (Forms_cookies_supported, value); }
- }
- static string default_url
- {
- get { return (string) AppDomain.CurrentDomain.GetData (Forms_default_url); }
- set { AppDomain.CurrentDomain.SetData (Forms_default_url, value); }
- }
- static bool enable_crossapp_redirects
- {
- get {
- object o = AppDomain.CurrentDomain.GetData (Forms_enable_crossapp_redirects);
- return o != null ? (bool) o : false;
- }
- set { AppDomain.CurrentDomain.SetData (Forms_enable_crossapp_redirects, value); }
- }
- static string login_url
- {
- get { return (string) AppDomain.CurrentDomain.GetData (Forms_login_url); }
- set { AppDomain.CurrentDomain.SetData (Forms_login_url, value); }
- }
-#else
static string cookie_domain;
static HttpCookieMode cookie_mode;
static bool cookies_supported;
static string default_url;
static bool enable_crossapp_redirects;
static string login_url;
-#endif
-#endif
// same names and order used in xsp
static string [] indexFiles = { "index.aspx",
"Default.aspx",
"default.aspx",
"index.html",
"index.htm" };
+ public static TimeSpan Timeout {
+ get; private set;
+ }
+
+ public static bool IsEnabled {
+ get { return initialized; }
+ }
+
+ public static void EnableFormsAuthentication (NameValueCollection configurationData)
+ {
+ BuildManager.AssertPreStartMethodsRunning ();
+ if (configurationData == null || configurationData.Count == 0)
+ return;
+
+ string value = configurationData ["loginUrl"];
+ if (!String.IsNullOrEmpty (value))
+ login_url = value;
+ value = configurationData ["defaultUrl"];
+ if (!String.IsNullOrEmpty (value))
+ default_url = value;
+ }
public FormsAuthentication ()
{
}
if (context == null)
throw new HttpException ("Context is null!");
-#if NET_2_0
+ name = name.ToLower (Helpers.InvariantCulture);
+
AuthenticationSection section = (AuthenticationSection) WebConfigurationManager.GetSection (authConfigPath);
FormsAuthenticationCredentials config = section.Forms.Credentials;
FormsAuthenticationUser user = config.Users[name];
if (user != null)
stored = user.Password;
-#else
- AuthConfig config = context.GetConfig (authConfigPath) as AuthConfig;
- Hashtable users = config.CredentialUsers;
- string stored = users [name] as string;
-#endif
+
if (stored == null)
return false;
+ bool caseInsensitive = true;
switch (config.PasswordFormat) {
- case FormsAuthPasswordFormat.Clear:
- /* Do nothing */
- break;
- case FormsAuthPasswordFormat.MD5:
- password = HashPasswordForStoringInConfigFile (password, "MD5");
- break;
- case FormsAuthPasswordFormat.SHA1:
- password = HashPasswordForStoringInConfigFile (password, "SHA1");
- break;
+ case FormsAuthPasswordFormat.Clear:
+ caseInsensitive = false;
+ /* Do nothing */
+ break;
+ case FormsAuthPasswordFormat.MD5:
+ password = HashPasswordForStoringInConfigFile (password, FormsAuthPasswordFormat.MD5);
+ break;
+ case FormsAuthPasswordFormat.SHA1:
+ password = HashPasswordForStoringInConfigFile (password, FormsAuthPasswordFormat.SHA1);
+ break;
}
- return (password == stored);
+ return String.Compare (password, stored, caseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) == 0;
}
static FormsAuthenticationTicket Decrypt2 (byte [] bytes)
if (protection == FormsProtectionEnum.None)
return FormsAuthenticationTicket.FromByteArray (bytes);
-#if NET_2_0
- MachineKeySection config = (MachineKeySection) WebConfigurationManager.GetSection (machineKeyConfigPath);
-#else
- MachineKeyConfig config = HttpContext.GetAppConfig (machineKeyConfigPath) as MachineKeyConfig;
-#endif
- bool all = (protection == FormsProtectionEnum.All);
-
- byte [] result = bytes;
- if (all || protection == FormsProtectionEnum.Encryption) {
- ICryptoTransform decryptor;
- decryptor = TripleDES.Create ().CreateDecryptor (config.DecryptionKey192Bits, init_vector);
- result = decryptor.TransformFinalBlock (bytes, 0, bytes.Length);
- bytes = null;
- }
-
- if (all || protection == FormsProtectionEnum.Validation) {
- int count;
- MachineKeyValidation validationType;
-
-#if NET_2_0
- validationType = config.Validation;
-#else
- validationType = config.ValidationType;
-#endif
- if (validationType == MachineKeyValidation.MD5)
- count = MD5_hash_size;
- else
- count = SHA1_hash_size; // 3DES and SHA1
-
-#if NET_2_0
- byte [] vk = config.ValidationKeyBytes;
-#else
- byte [] vk = config.ValidationKey;
-#endif
- byte [] mix = new byte [result.Length - count + vk.Length];
- Buffer.BlockCopy (result, 0, mix, 0, result.Length - count);
- Buffer.BlockCopy (vk, 0, mix, result.Length - count, vk.Length);
-
- byte [] hash = null;
- switch (validationType) {
- case MachineKeyValidation.MD5:
- hash = MD5.Create ().ComputeHash (mix);
- break;
- // From MS docs: "When 3DES is specified, forms authentication defaults to SHA1"
- case MachineKeyValidation.TripleDES:
- case MachineKeyValidation.SHA1:
- hash = SHA1.Create ().ComputeHash (mix);
- break;
- }
-
- if (result.Length < count)
- throw new ArgumentException ("Error validating ticket (length).", "encryptedTicket");
-
- int i, k;
- for (i = result.Length - count, k = 0; k < count; i++, k++) {
- if (result [i] != hash [k])
- throw new ArgumentException ("Error validating ticket.", "encryptedTicket");
- }
+ MachineKeySection config = (MachineKeySection) WebConfigurationManager.GetWebApplicationSection (machineKeyConfigPath);
+ byte [] result = null;
+ if (protection == FormsProtectionEnum.All) {
+ result = MachineKeySectionUtils.VerifyDecrypt (config, bytes);
+ } else if (protection == FormsProtectionEnum.Encryption) {
+ result = MachineKeySectionUtils.Decrypt (config, bytes);
+ } else if (protection == FormsProtectionEnum.Validation) {
+ result = MachineKeySectionUtils.Verify (config, bytes);
}
return FormsAuthenticationTicket.FromByteArray (result);
public static FormsAuthenticationTicket Decrypt (string encryptedTicket)
{
- if (encryptedTicket == null || encryptedTicket == String.Empty)
+ if (String.IsNullOrEmpty (encryptedTicket))
throw new ArgumentException ("Invalid encrypted ticket", "encryptedTicket");
Initialize ();
FormsAuthenticationTicket ticket;
-#if NET_2_0
- byte [] bytes = MachineKeySection.GetBytes (encryptedTicket, encryptedTicket.Length);
-#else
- byte [] bytes = MachineKeyConfig.GetBytes (encryptedTicket, encryptedTicket.Length);
-#endif
+ byte [] bytes = Convert.FromBase64String (encryptedTicket);
+
try {
ticket = Decrypt2 (bytes);
} catch (Exception) {
Initialize ();
byte [] ticket_bytes = ticket.ToByteArray ();
if (protection == FormsProtectionEnum.None)
- return GetHexString (ticket_bytes);
-
- byte [] result = ticket_bytes;
-#if NET_2_0
- MachineKeySection config = (MachineKeySection) WebConfigurationManager.GetSection (machineKeyConfigPath);
-#else
- MachineKeyConfig config = HttpContext.GetAppConfig (machineKeyConfigPath) as MachineKeyConfig;
-#endif
- bool all = (protection == FormsProtectionEnum.All);
- if (all || protection == FormsProtectionEnum.Validation) {
- byte [] valid_bytes = null;
-#if NET_2_0
- byte [] vk = config.ValidationKeyBytes;
-#else
- byte [] vk = config.ValidationKey;
-#endif
- byte [] mix = new byte [ticket_bytes.Length + vk.Length];
- Buffer.BlockCopy (ticket_bytes, 0, mix, 0, ticket_bytes.Length);
- Buffer.BlockCopy (vk, 0, mix, result.Length, vk.Length);
-
- switch (
-#if NET_2_0
- config.Validation
-#else
- config.ValidationType
-#endif
- ) {
- case MachineKeyValidation.MD5:
- valid_bytes = MD5.Create ().ComputeHash (mix);
- break;
- // From MS docs: "When 3DES is specified, forms authentication defaults to SHA1"
- case MachineKeyValidation.TripleDES:
- case MachineKeyValidation.SHA1:
- valid_bytes = SHA1.Create ().ComputeHash (mix);
- break;
- }
+ return Convert.ToBase64String (ticket_bytes);
- int tlen = ticket_bytes.Length;
- int vlen = valid_bytes.Length;
- result = new byte [tlen + vlen];
- Buffer.BlockCopy (ticket_bytes, 0, result, 0, tlen);
- Buffer.BlockCopy (valid_bytes, 0, result, tlen, vlen);
- }
+ byte [] result = null;
+ MachineKeySection config = (MachineKeySection) WebConfigurationManager.GetWebApplicationSection (machineKeyConfigPath);
- if (all || protection == FormsProtectionEnum.Encryption) {
- ICryptoTransform encryptor;
- encryptor = TripleDES.Create ().CreateEncryptor (config.DecryptionKey192Bits, init_vector);
- result = encryptor.TransformFinalBlock (result, 0, result.Length);
+ if (protection == FormsProtectionEnum.All) {
+ result = MachineKeySectionUtils.EncryptSign (config, ticket_bytes);
+ } else if (protection == FormsProtectionEnum.Encryption) {
+ result = MachineKeySectionUtils.Encrypt (config, ticket_bytes);
+ } else if (protection == FormsProtectionEnum.Validation) {
+ result = MachineKeySectionUtils.Sign (config, ticket_bytes);
}
- return GetHexString (result);
+ return Convert.ToBase64String (result);
}
public static HttpCookie GetAuthCookie (string userName, bool createPersistentCookie)
DateTime now = DateTime.Now;
DateTime then;
- if (createPersistentCookie)
- then = now.AddYears (50);
- else
- then = now.AddMinutes (timeout);
+ if (createPersistentCookie)
+ then = now.AddMinutes(timeout);
+ else
+ then = DateTime.MinValue;
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket (1,
userName,
now,
- then,
+ createPersistentCookie?then:now.AddYears (50),
createPersistentCookie,
String.Empty,
cookiePath);
- if (!createPersistentCookie)
- then = DateTime.MinValue;
-
HttpCookie cookie = new HttpCookie (cookieName, Encrypt (ticket), strCookiePath, then);
if (requireSSL)
cookie.Secure = true;
+ if (!String.IsNullOrEmpty (cookie_domain))
+ cookie.Domain = cookie_domain;
+
return cookie;
}
- internal static string ReturnUrl
- {
+ internal static string ReturnUrl {
get { return HttpContext.Current.Request ["RETURNURL"]; }
}
return returnUrl;
}
- static string GetHexString (byte [] bytes)
+ static string HashPasswordForStoringInConfigFile (string password, FormsAuthPasswordFormat passwordFormat)
{
- StringBuilder result = new StringBuilder (bytes.Length * 2);
- foreach (byte b in bytes)
- result.AppendFormat ("{0:X2}", (int) b);
+ if (password == null)
+ throw new ArgumentNullException ("password");
+
+ byte [] bytes;
+ switch (passwordFormat) {
+ case FormsAuthPasswordFormat.MD5:
+ bytes = MD5.Create ().ComputeHash (Encoding.UTF8.GetBytes (password));
+ break;
- return result.ToString ();
- }
+ case FormsAuthPasswordFormat.SHA1:
+ bytes = SHA1.Create ().ComputeHash (Encoding.UTF8.GetBytes (password));
+ break;
+
+ default:
+ throw new ArgumentException ("The format must be either MD5 or SHA1", "passwordFormat");
+ }
+ return MachineKeySectionUtils.GetHexString (bytes);
+ }
+
public static string HashPasswordForStoringInConfigFile (string password, string passwordFormat)
{
if (password == null)
if (passwordFormat == null)
throw new ArgumentNullException ("passwordFormat");
- byte [] bytes;
- if (String.Compare (passwordFormat, "MD5", true, CultureInfo.InvariantCulture) == 0) {
- bytes = MD5.Create ().ComputeHash (Encoding.UTF8.GetBytes (password));
- } else if (String.Compare (passwordFormat, "SHA1", true, CultureInfo.InvariantCulture) == 0) {
- bytes = SHA1.Create ().ComputeHash (Encoding.UTF8.GetBytes (password));
+ if (String.Compare (passwordFormat, "MD5", StringComparison.OrdinalIgnoreCase) == 0) {
+ return HashPasswordForStoringInConfigFile (password, FormsAuthPasswordFormat.MD5);
+ } else if (String.Compare (passwordFormat, "SHA1", StringComparison.OrdinalIgnoreCase) == 0) {
+ return HashPasswordForStoringInConfigFile (password, FormsAuthPasswordFormat.SHA1);
} else {
throw new ArgumentException ("The format must be either MD5 or SHA1", "passwordFormat");
}
-
- return GetHexString (bytes);
}
public static void Initialize ()
if (initialized)
return;
-#if NET_2_0
AuthenticationSection section = (AuthenticationSection)WebConfigurationManager.GetSection (authConfigPath);
FormsAuthenticationConfiguration config = section.Forms;
cookieName = config.Name;
+ Timeout = config.Timeout;
timeout = (int)config.Timeout.TotalMinutes;
cookiePath = config.Path;
protection = config.Protection;
cookie_domain = config.Domain;
cookie_mode = config.Cookieless;
cookies_supported = true; /* XXX ? */
- default_url = MapUrl(config.DefaultUrl);
+ if (!String.IsNullOrEmpty (default_url))
+ default_url = MapUrl (default_url);
+ else
+ default_url = MapUrl(config.DefaultUrl);
enable_crossapp_redirects = config.EnableCrossAppRedirects;
- login_url = MapUrl(config.LoginUrl);
-#else
- HttpContext context = HttpContext.Current;
- AuthConfig authConfig = context.GetConfig (authConfigPath) as AuthConfig;
- if (authConfig != null) {
- cookieName = authConfig.CookieName;
- timeout = authConfig.Timeout;
- cookiePath = authConfig.CookiePath;
- protection = authConfig.Protection;
-#if NET_1_1
- requireSSL = authConfig.RequireSSL;
- slidingExpiration = authConfig.SlidingExpiration;
-#endif
- } else {
- cookieName = ".MONOAUTH";
- timeout = 30;
- cookiePath = "/";
- protection = FormsProtectionEnum.All;
-#if NET_1_1
- slidingExpiration = true;
-#endif
- }
-#endif
-
- // IV is 8 bytes long for 3DES
- init_vector = new byte [8];
- int len = cookieName.Length;
- for (int i = 0; i < 8; i++) {
- if (i >= len)
- break;
-
- init_vector [i] = (byte) cookieName [i];
- }
+ if (!String.IsNullOrEmpty (login_url))
+ login_url = MapUrl (login_url);
+ else
+ login_url = MapUrl(config.LoginUrl);
initialized = true;
}
}
-#if NET_2_0
static string MapUrl (string url) {
if (UrlUtils.IsRelativeUrl (url))
return UrlUtils.Combine (HttpRuntime.AppDomainAppVirtualPath, url);
else
return UrlUtils.ResolveVirtualPathFromAppAbsolute (url);
}
-#endif
public static void RedirectFromLoginPage (string userName, bool createPersistentCookie)
{
HttpCookieCollection cc = response.Cookies;
cc.Remove (cookieName);
- HttpCookie expiration_cookie = new HttpCookie (cookieName, "");
+ HttpCookie expiration_cookie = new HttpCookie (cookieName, String.Empty);
expiration_cookie.Expires = new DateTime (1999, 10, 12);
expiration_cookie.Path = cookiePath;
+ if (!String.IsNullOrEmpty (cookie_domain))
+ expiration_cookie.Domain = cookie_domain;
cc.Add (expiration_cookie);
-
-#if NET_2_0
Roles.DeleteCookie ();
-#endif
}
public static string FormsCookieName
return cookiePath;
}
}
-#if NET_1_1
+
public static bool RequireSSL {
get {
Initialize ();
return slidingExpiration;
}
}
-#endif
-#if NET_2_0
public static string CookieDomain {
get { Initialize (); return cookie_domain; }
}
Redirect (LoginUrl + "?" + extraQueryString);
}
- private static void Redirect (string url)
+ static void Redirect (string url)
{
HttpContext.Current.Response.Redirect (url);
}
-#endif
- private static void Redirect (string url, bool end)
+ static void Redirect (string url, bool end)
{
HttpContext.Current.Response.Redirect (url, end);
}