ASP.NET aspx page code runs impersonated though impersonation is disabled - asp.net

I have a blank test app created in VS 2005 as ASP.NET application. MSDN says that
By default, ASP.NET does not use impersonation, and your code runs using the ASP.NET application's process identity.
And I have the following web.config
<configuration>
<appSettings/>
<connectionStrings/>
<system.web>
<!--
Set compilation debug="true" to insert debugging
symbols into the compiled page. Because this
affects performance, set this value to true only
during development.
-->
<compilation debug="true" defaultLanguage="c#" />
<!--
The <authentication> section enables configuration
of the security authentication mode used by
ASP.NET to identify an incoming user.
-->
<authentication mode="Windows"/>
<identity impersonate="false"/>
<!--
The <customErrors> section enables configuration
of what to do if/when an unhandled error occurs
during the execution of a request. Specifically,
it enables developers to configure html error pages
to be displayed in place of a error stack trace.
<customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
<error statusCode="403" redirect="NoAccess.htm" />
<error statusCode="404" redirect="FileNotFound.htm" />
</customErrors>
-->
</system.web>
</configuration>
So it seem impersonation is disabled just like the article is suggesting.
My aspx is blank default and the codebehind is
namespace TestWebapp
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine(String.Format("Before1: Current Princupal = {0}", Thread.CurrentPrincipal.Identity.Name));
WindowsImpersonationContext ctx = WindowsIdentity.Impersonate(IntPtr.Zero);
try
{
int a = 0;
System.Diagnostics.Debug.WriteLine(String.Format("After: Current Princupal = {0}", Thread.CurrentPrincipal.Identity.Name));
} finally
{
ctx.Undo();
}
}
}
}
When I reload the page I get the following debug output:
[5288] Before1: Current Princupal =
DOMAIN\User
[5288] After: Current Princupal =
DOMAIN\User
Output is the same with
<identity impersonate="false"/>
The web site uses Default Application Pool and the pool is set up to use NETWORK SERVICE account for its worker processes.
I'm sure the application uses the web.config it should use and the w3p.exe worker process is running under NETWORK SERVICE.
What can be wrong in this case?
Thanks!
#Edit: Rob, thanks for the tip!
The $user shortcut shows me that everything is happening as I expect: with impersonation on I have the process running user NT AUTHORITY\NETWORK SERVICE and the thread has DOMAIN\User before WindowsIdentity.Impersonate(IntPtr.Zero) and "No Token. Thread not impersonating." after.
But Thread.CurrentPrincipal.Identity.Name and HttpContext.Current.User.Identity.Name still give me DOMAIN\User in both places.
#Edit: I've found out that to get Thread.CurrentPrincipal and HttpContext.Current.User changed I have to manually do it:
Thread.CurrentPrincipal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
HttpContext.Current.User = Thread.CurrentPrincipal;
I'm not sure what's the point here, but anyway. I now have a problem with sharepoint shared services manage user profile permission but that's another question.

I think I understand your problem here.
Things to know before moving further,
There are different security context while an application is running. Like System.Security.Principal.WindowsIdentity.GetCurrent().Name, and the one you mentioned above, i.e. System.Threading.Thread.CurrentPrincipal.Identity.Name
In a web application, System.Threading.Thread.CurrentPrincipal.Identity is always provided by HttpContext.Current.User.Identity.
Coming to your point. If you want to modify System.Threading.Thread.CurrentPrincipal.Identity, then modify HttpContext.Current.User.Identity which initially provided by your authentication mechanism.

Seems odd, A few things to try:
While in on a breakpoint in Debug type $user in a watch window, that will show you the process and thread identities.
Your use of impersonate is incorrect, try this code:
// Declare the logon types as constants
const long LOGON32_LOGON_INTERACTIVE = 2;
const long LOGON32_LOGON_NETWORK = 3;
// Declare the logon providers as constants
const long LOGON32_PROVIDER_DEFAULT = 0;
const long LOGON32_PROVIDER_WINNT50 = 3;
const long LOGON32_PROVIDER_WINNT40 = 2;
const long LOGON32_PROVIDER_WINNT35 = 1;
[DllImport("advapi32.dll", EntryPoint = "LogonUser")]
private static extern bool LogonUser(
string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
public static WindowsImpersonationContext ImpersonateCurrentUserBegin(System.Net.NetworkCredential credential)
{
WindowsImpersonationContext impersonationContext = null;
if (credential == null || credential.UserName.Length == 0 || credential.Password.Length == 0 || credential.Domain.Length == 0)
{
throw new Exception("Incomplete user credentials specified");
}
impersonationContext = Security.Impersonate(credential);
if (impersonationContext == null)
{
return null;
}
else
{
return impersonationContext;
}
}
public static void ImpersonateCurrentUserEnd(WindowsImpersonationContext impersonationContext)
{
if (impersonationContext != null)
{
impersonationContext.Undo();
}
}

What does HttpContext.User.Identity.Name give you?
Assume you've checked the security tab within IIS that it allows anonymous access?
Are you within an active directory that has some strange local policy?

Related

ASP.Net Web Application Request.Params["AUTH_USER"] is empty string

I'm building an ASP.Net 4.5 Web Application in Visual Studio 2012. I've put the following code in my Global.asax.cs:
protected void Session_Start(object sender, EventArgs e)
{
Session["LastError"] = "";
// Code that runs when a new session is started
string delimStr = #"\";
char[] delimiter = delimStr.ToCharArray();
string[] requestor = Request.Params["AUTH_USER"].ToString().Split(delimiter, 2);
string requestorEID = requestor[1];
Session["UserID"] = requestorEID;
//Get User data (name, roleid, rolename) from database
List<SqlParameter> paramList = new List<SqlParameter>();
paramList.Add(new SqlParameter("#EmployeeID", requestorEID));
DataTable dtUserList = KLClassLibrary.KLDataAccessLayer.ExecuteSelect("GetUserData", paramList, "OffSiteCN");
if (dtUserList.Rows.Count > 0)
{
Session["EmployeeName"] = dtUserList.Rows[0]["EmployeeName"].ToString();
}
else
{
Session["LastError"] = "You Are Not Authorized To Access This Application";
Session["EmployeeName"] = "";
Response.Redirect("~/ErrorPage.aspx");
}
}
Whenever my code gets to the line
string[] requestor = Request.Params["AUTH_USER"].ToString().Split(delimiter, 2);
Request.Params["AUTH_USER"] is equal to an empty string. I've used this code successfully for years, although I'm new to VS2012 and .Net 4.5. What's wrong?
This isn't a complete solution, but I was able to create a workaround by using the following code:
string requestor = Request.LogonUserIdentity.Name.ToString();
int requestor_length = requestor.Length;
string requestorEID = requestor.Substring(requestor_length - 5, 5);
Session["UserID"] = requestorEID;
Ensure you have "Windows Authentication" enabled in the project's properties (it is the very bottom of the properties list). This will allow IISExpress to use windows authentication.
Then ensure you have denied access to anonymous users in your web.config's system.web authorization section, system.webServer or both.
For IIS 6.5 or below:
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
For IIS 7 or above
<system.webServer>
<security>
<authorization>
<add accessType="Deny" users="?"/>
</authorization>
</security>
</system.webServer>
you can also set the windows authentication modes in these sections, but the server must allow you to override their default setting. IISExpress is not set to allow this value to be overridden so you can not try to set it here for your debug mode.
The older versions of Visual Studio used Cassini to emulate a web server for your debugging. Now it uses IISExpress, which will deliver you a closer experience to a true IIS server. There used to be certain scenarios that would differ between Cassini and your actual IIS server - now it is rare to non-existent that I run into these issue. However with correct configuration settings you can get your AUTH_USER value on session start.

Custom Role Provider not called. What am I doing wrong?

So I'm trying to create a Hello World custom Role Provider-solution in ASP.NET MVC 4.
Basically I've set authentication mode="Windows" in web.config along with defining a role provider like this:
<roleManager enabled="true" defaultProvider="MyRoleProvider">
<providers>
<clear />
<add name="MyRoleProvider" type="MyProject.Code.MyRoleProvider" />
</providers>
</roleManager>
Then I've decorated the About controller method like this:
[Authorize(Roles = "SomeRole")]
public ActionResult About()
{ /* ... */ }
The custom RoleProvider-class looks like this:
namespace MyProject.Code {
public class MyRoleProvider : RoleProvider
{
public override bool IsUserInRole(string username, string roleName)
{
if (roleName == "SomeRole" && username = "Administrator") return true;
return false;
}
public override string[] GetRolesForUser(string username)
{
return new string[] { "SomeRole" };
}
/* a bunch of other overridden methods simply throwing not implementedException */
}
}
Now the thing is I've put a breakpoint on every single executable line of code in MyRoleProvder but none are hit. I have tested that breakpoints elsewhere are hit so the debugger is not the problem. Why isn't my code in the role provided executed? I was expecting IsUserInRole and/or GetRolesForUser to be executed when I navigate to the About-page. Am I wrong? Have I configured something wrong?
Full web.config for reference
edit: The user is redirected to the login page when the about page is clicked. I now realize this is due to the user actually is not authenticated yet. Authorization naturally happens after authentication. Is IISExpress not providing Windows identity?
As this is the first result in a google search i want to give a solution, maybe for others:
To use the windows identity in IIEExpress you have to activate it in your applicationhost.config , which is located in
[SolutionDir].vs\config\applicationhost.config
there you can find the xml tag
<configuration>
<system.webServer>
<security>
<authentication>
<windowsAuthentication enabled="false">
change enabled="false" to enabled="true"
and you can use windows authenticatuion in IISExpress
I think your type declaration is incomplete, you should include both the full name and the assembly name.
type="MyProject.Code.MyRoleProvider, MyProject"
You might also need to set Version, Culture and PublicKeyToken if your assemblies are place in the GAC
Hope this helps

Change Custom Error Mode in Web Config Programmatically in ASP.NET MVC 3

How can I change the Custom error mode in web.config file programmatically? I need to change the following:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<configSections />
...
<system.web>
....
<customErrors mode="RemoteOnly">
<error statusCode="404" redirect="~/errors/404" />
</customErrors>
To
<customErrors mode="off">
<error statusCode="404" redirect="~/errors/404" />
</customErrors>
Does any one have any idea about this?
UPDATE
As CoDe aDDict answer, I try to use this:
Configuration config = WebConfigurationManager.OpenWebConfiguration("~");
CustomErrorsSection CustomErrorsection = (CustomErrorsSection)config.GetSection("system.web/customErrors");
CustomErrorsection.Mode = CustomErrorsMode.Off;
config.Save();
But there is an exception:
Access to the path 'D:\Projects\MyProject\web.config' is denied.
I found an example some days ago which change alot in custom error configration
public static void ConfigureCustomErrors()
{
Configuration config =
WebConfigurationManager.OpenWebConfiguration("~");
CustomErrorsSection section =
(CustomErrorsSection)config.GetSection(
"system.web/customErrors");
//Verify that customErrors exists in web.config
if (section != null)
{
//Only configure if customErrors is enabled
if (section.Mode != CustomErrorsMode.Off)
{
if(!section.IsReadOnly() &&
!section.SectionInformation.IsLocked)
{
//Collection of new redirects to add to
//the customErrors element
CustomErrorCollection redirectsToAdd =
new CustomErrorCollection();
//Page ID of the page to be used for
//custom error redirects
int redirectPageId = 0;
//Get existing redirects, if any
CustomError redirect404 =
section.Errors["404"];
CustomError redirect500 =
section.Errors["500"];
//Get URL for 404 redirects
int.TryParse(
ConfigurationManager.AppSettings[
"FileNotFoundPageId"],
out redirectPageId);
string fileNotFoundURL =
ToolBox.GetSimpleAddress(
DataFactory.Instance.GetPage(
new PageReference(redirectPageId));
//Get URL for server error redirects
int.TryParse(
ConfigurationManager.AppSettings[
"GenericErrorPageId"],
out redirectPageId);
string serverErrorURL =
ToolBox.GetSimpleAddress(
DataFactory.Instance.GetPage(
new PageReference(redirectPageId)));
//If the 404 redirect hasn't been
//specified or if its redirect
//URL is invalid
if (fileNotFoundURL!=string.Empty &&
(redirect404 == null ||
redirect404.Redirect!=
fileNotFoundURL))
{
//Add new
if (redirect404 == null)
{
CustomError fileNotFoundError =
new CustomError(404,
fileNotFoundURL);
redirectsToAdd.Add(
fileNotFoundError);
}
else //Modify existing
{
redirect404.Redirect =
fileNotFoundURL;
}
}
//If the 500 redirect hasn't been
//specified or if its redirect
//URL is invalid
if (fileNotFoundURL != string.Empty &&
(redirect500 == null ||
redirect500.Redirect !=
fileNotFoundURL))
{
//Add new
if (redirect500 == null)
{
CustomError serverError =
new CustomError(500,
serverErrorURL);
redirectsToAdd.Add(serverError);
}
else //Modify existing redirect
{
redirect500.Redirect =
serverErrorURL;
}
}
//Add any new redirects
foreach (
CustomError redirectToAdd in
redirectsToAdd)
{
section.Errors.Add(redirectToAdd);
}
//Save web.config if its
//contents have changed
config.Save();
}
}
}
}
In order to update your web.config file, the process/identity that is running your app pool will need to have modify (write) access to the web.config file. You are getting an error because your application does not have permission to write to the web.config file. Add the proper permissions and what has been stated will work without exception. Now with that said...
DISCLAIMER
The config file drives your application, has authentication/authorization information for paths in your app, a machine key (perhaps) for encoding/decoding authentication tickets, SQL connection strings, etc. Having this be writable by your application could be a nasty security concern. I have made my disclaimer. Take caution :)

How to override web.config customErrors settings at runtime?

I have the following code in the web.config file for my ASP.NET C# app that's hosed on Azure:
<!-- Turn on Custom Errors -->
<!-- Switch the mode to RemoteOnly for Retail/Production -->
<!-- Switch the mode to On for to see error pages in the IDE during development -->
<customErrors mode="On" defaultRedirect="ErrorPage.aspx">
<error statusCode="403" redirect="ErrorPage403.aspx"/>
<error statusCode="404" redirect="ErrorPage404.aspx"/>
</customErrors>
This works great for errors when I'm hitting my site site natively (http://ipredikt.com/ErrorPage.aspx), but I also have a Facebook version of the app in which all of the pages use a different MasterPage and hence a different URL (http://ipredikt.com/ErrorPageFB.aspx).
Is it possible to modify the customError redirect values at runtime when I'm running in Facebook app mode, as if I had the following settings in web.config:
<customErrors mode="On" defaultRedirect="ErrorPageFB.aspx">
<error statusCode="403" redirect="ErrorPage403FB.apx"/>
<error statusCode="404" redirect="ErrorPage404FB.apx"/>
</customErrors>
I don't think I can set this at the Application scope since it's individual pages in my app that have knowledge of whether they are running in Facebook mode.
Hi I think what you can do is make another redirect inside your custom error page acording to the referrer - Request.UrlReferrer sometime the referrer is null so make sure you deal with that
So here's a brute force solution. I'm using this on the page for the non-Facebook mode 404 errors:
protected override void OnInit(System.EventArgs e)
{
// If the user tries, for example, to navigate to" /fb/foo/bar
// then the Request.Url.Query will be as follows after the 404 error: ?aspxerrorpath=/fb/foo/bar
string queryString = Request.RequestContext.HttpContext.Request.Url.Query;
string[] str = queryString.Split('=');
if (str.Length > 0)
{
string[] str2 = str[1].Split('/');
if (str2.Length > 1)
{
string test = str2[1].ToLowerInvariant();
if (test == "fb")
{
string pathAndQuery = Request.RequestContext.HttpContext.Request.Url.PathAndQuery;
string absolutePath = Request.RequestContext.HttpContext.Request.Url.AbsolutePath;
string mungedVirtualPath = pathAndQuery.Replace(absolutePath, "/ErrorPage404FB.aspx");
Response.Redirect(mungedVirtualPath);
}
}
}
base.OnInit(e);
}
Hardly ideal, but it works.
"Facebook mode" seems like something you could track in Session, which would be accessible in ErrorPage.aspx to trigger a transfer to ErrorPageFB.aspx.
Update - you can clean up your brute-force solution quite a bit by using Request.QueryString:
protected override void OnInit(System.EventArgs e)
{
// If the user tries, for example, to navigate to" /fb/foo/bar
// then the Request.Url.Query will be as follows after the 404 error: ?aspxerrorpath=/fb/foo/bar
var requestedPath = Request.RequestContext.HttpContext.Request.QueryString["aspxerrorPath"];
if (requestedPath.StartsWith("/fb/", StringComparison.OrdinalIgnoreCase))
{
var requestedUrl = Request.RequestContext.HttpContext.Request.Url;
var pathAndQuery = requestedUrl.PathAndQuery;
var absolutePath = requestedUrl.AbsolutePath;
var mungedVirtualPath = pathAndQuery.Replace(absolutePath, "/ErrorPage404FB.aspx");
Response.Redirect(mungedVirtualPath);
}
base.OnInit(e);
}
Does Request.RequestContext.HttpContext.Request actually return a different instance than simply Request?
The easy way to do it is using session but if you don't use session on you website you can always use cookie and when the user arrive to the error page examine the cookie and decide if you want to redirect him to a new error page

Impersonation WCF

I have a WCF service, hosted in IIS, which I require to impersonate the annon account.
in my Webconfig
<authentication mode="Windows"/>
<identity impersonate ="true"/>
Testing the following, with vs2008
public void ByRuleId(int ruleId)
{
try
{
string user = WindowsIdentity.GetCurrent().Name;
string name = Thread.CurrentPrincipal.Identity.Name;
........
//get the data as a string.
using (FileStream fs = File.Open(location, FileMode.Open))
using (StreamReader reader = new StreamReader(fs))
{
rawData = reader.ReadToEnd();
}
}
catch.....
}
this works. however if I add impersonation attribute
[OperationBehavior(Impersonation=ImpersonationOption.Required)]
public void ByRuleId(int ruleId)
this does not work with the error message
"Either a required impersonation level was not provided, or the provided impersonation level is invalid."
a little poking around I noticed the first way was authenticated by Kerboros and the second way just failed on authentication type
I am using the WCF client tool, to pass my credentials. this seems to be working.
Check the 'TokenImpersonationLevel' of identity of the current thread; you'll need it to be at least 'Impersonation' to perform operations on the machine that the service is running on.
Typically, if you are using a proxy client, you'll need to set the 'TokenImpersonationLevel' of the client:
http://www.devx.com/codemag/Article/33342/1763/page/4
the main goal of this was to get anon access, even tho MattK answer was a great help.
here is what i did to do so.
on the implementation of the WCF contract I added the
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class TransferFile : ITransferFile
and in the web.config
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled ="true" />
after this i was able to impersonate the anon account

Resources