ASP.NET Impersonation by Role - asp.net

I modified the ASP.NET login control to also allow specifying UserRole ('Employee' or 'Volunteer'). Users are authenticated via a call to a webservice written by our client, which accepts username/password/role and returns true or false.
If role is 'Employee' it represents an active directory user. The application should impersonate the user with the given username/password.
If role is 'Volunteer' the application should run under a set Windows account whose username/password are known in advance (i.e. hard-coded in web.config file).
The server runs on Windows Server 2003. I am confused by the myriad of configuration choices and trying to understand my options;
Is it possible to have multiple scenarios as described?
Should I specify the impersonation programmatically or can it be done through the config file? If so, is it required to use LogonUser or WindowsIdentity?
What config file setup should I use? (i.e. Forms authentication, impersonate=true, etc..)
Thank you in advance.

Because the decision about which identity to impersonate is based on run-time data, you'll likely have to deal with impersonation programmatically.
I use a combination of interop and WindowsIdentity to handle impersonation. The steps I follow are:
Log on using the interop LogonUserA(), which fills a handle to an IntPtr (token).
Duplicate the token with the interop DuplicateToken().
Create a new windows identity, a la: var identity = new WindowsIdentity(tokenDuplicate);.
Create an impersonation context via: var context = identity.Impersonate();
Close both tokens with the interop CloseHandle()
When finished impersonating, undo the impersonation context via: context.Undo();
I keep a disposable class around to handle the details. Steps 1-5 occur in a constructor, and step 6 occurs in the dispose routine. This helps ensure that I properly revert even in the face of an exception.
With this approach, since you are passing credentials via a service method, the web.config authentication scheme is not entirely forced. If, however, you are using integrated Windows auth, you could programmatically impersonate the current user from HttpContext.Current.User.Identity.Impersonate(), without passing credentials in a service method.
On an aside, and you may already know, PInvoke.net is a valuable resource for configuring signatures for interop methods.

Related

Login fails for user '' -- using an ASP.NET SqlDataSource in Azure using a managed identity

I'm working on an old webpage which the client is trying to migrate to Azure. The app has worked forever, so it's not a question of "maybe the code is broken". Instead, it's the migration to Azure that's been throwing up issues. The differences between a localhost build and the Azure build are significant, since I don't have a Managed Identity local. But the differences are primarily configuration, other than bits of code to add an Access Token to SQL connections.
The app uses dozens of asp:SqlDataSource objects defined in .aspx pages to do all sorts of work. But that's fine, they use a standard connection string, pulled from the web.config, to talk to the database:
<add name="MyConnectionString"
connectionString="Server=tcp:MyDatabaseServer.database.windows.net,1433;
Initial Catalog=MyDatabaseName;
Persist Security; Info=False; MultipleActiveResultSets=False;
Encrypt=True; TrustServerCertificate=False;
Connection Timeout=30" providerName="System.Data.SqlClient"/>
For security reasons, the client now has to use a Managed Identity to talk to the database, rather than our previous service account model.
I added an OnSelecting event to the SqlDataSource where I set an access token on the connection. I'm not getting any exceptions or complaints about that code, so either it's running fine or it's not running at all. The managed identity is created, and it has reader, writer, and executor roles on the database, but this bit of code is the first time that the managed identity is being put through its paces.
What I get is an error
[SqlException (0x80131904): Login failed for user ''.]
Is there some other permissions or flags I need to set on the managed identity, or some missing configuration? Should I be using a different event than the OnSelecting event? Is the URL for an access token different in this situation? Is there a place to find more detailed error logs?
First note that I haven't seen any possible way to use SQL-based session state with a managed identity. Switch your Session storage to InProc and see if your login problem goes away.
Second, I've found that making a web request to 169.254.169.254 to obtain a token doesn't work in a sovereign Azure environment. Instead, do this:
var credential = new Azure.Identity.DefaultAzureCredential();
var token = credential.GetToken(new Azure.Core.TokenRequestContext(new[] { "https://database.windows.net/.default" }));
connection.AccessToken = token.Token;
Using the OnSelecting event to assign the access token to the connection will work fine for a SqlDataSource. Instead of creating credentials and getting a token every time a stored procedure is called, store the token in the app and just re-use it; and make sure to purge the token when the user logs out.

Session state access in Web API across domains

I have a ASP.Net API implementation, where to store and access the data / variables across consecutive calls, I am using a session state object as shown below and it can be successfully accessed in the multiple calls to separate calls by a browser:
// Access the current session object
var blSession = HttpContext.Current.Session;
// create the BL object using the user id
BL accessBL = new BL(userID);
// Store the Bl object in the session object dictionary
blSession["UserBL"] = accessBL;
I have to enable the following setting in the Global.asax, for the Session object to be accessible:
protected void Application_PostAuthorizeRequest()
{
// Enable session state in the web api post authorization
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}
Issue comes in when the WebAPI shown above has to be accessed via another ASP.Net MVC client, which is separately hosted on a different machine, at that time same consecutive calls do not maintain the state as shown above and thus it leads to an exception, since the consecutive calls rely on session data to proceed.
I have seen a similar issue when I seen the similar issue when I use the Fiddler Debugger, as it gets hosted as a web proxy, so consecutive calls through that too fails, since it does not maintain the state. In my understanding, issue is due to setting the Cookie across domain, which doesn't seem to work across domains due to security reason
I know a workaround is to use an application wide variable like Cache, but please suggest if you have a way to get the SessionState work. Let me know if you need more details.
If you have not setup an alternative way to do SessionState, then the default behavior is to do it InMemory on the server. This is why you are seeing issues when the request is handled by a different ASP.NET server.
Web API calls are meant to be stateless. That is, they should not perform like a classic ASP.NET application that relies on the framework to store user specific information in Session variables across HTTP requests. For each call, pass in a user-specific identifier or token that you can then use to lookup information stored in your backend. You can store this information in your database or a distributed cache like MemCache for faster retrieval.

Using connection string login and not Application Pool Identity

The Question:
How do I get my Web Application to use my connection string login and not the Application Pool Identity?
The Background:
I have started up a .net mvc project and currently use Arvixe for hosting. I made a simple page using a form to communicate with the SQL database. Here is the site.
Just type anything into the text boxes and hit the button. If the communication is successful, it should just return a message.
The error:
Cannot open database "jfphotography" requested by the login. The login failed.
Login failed for user 'SEAWEED\photosbyjoefor_web6'.
My database user is jfphoto_dbadmin. I have tried making it photosbyjoefor_web6, but with no luck. No where in my code do I use, or set anything to, photosbyjoefor_web6. I have tried using photosbyjoefor_web6 in my connection string.
My connection string is correct, and I have been chatting with support for a week now. We believe that the application is using the Application Pool identity, and, using basic services, I am not allowed/Arvixe will not make any custom Application Pool Identities (which makes sense for shared hosting). Unfortunately, I do not want to be paying 40 bucks a month for their private hosting to be able to edit the Application Pools.
My project is using a Code-First approach, data service layers, context layer, all the fun stuff. I have never encountered this type of error before, and I am unsure of how to approach it. I really do not want to re-build my project in a different manner, but if I have to so-be-it.
Connection string:
add name="PhotographyContext" connectionString="Data Source=seaweed.arvixe.com;Initial Catalog=jfphotography;Integrated Security=True;user id=jfphoto_dbadmin;password=**" providerName="System.Data.SqlClient"

Can't run Windows service from ASP.NET page?

I'm trying to start a Windows service on Windows Server 2003
from an ASP.NET page:
the code of line 35 is:
32. Dim controller As New ServiceController
33. controller.MachineName = System.Environment.MachineName
34. controller.ServiceName = "WindowsServiceRealName"
35. controller.Start()
The error code is
System.InvalidOperationException: Cannot open WindowsServiceRealName service on computer 'DARWIN'. ---> System.ComponentModel.Win32Exception: Access is denied --- End of inner exception stack trace --- at System.ServiceProcess.ServiceController.GetServiceHandle(Int32 desiredAccess) at System.ServiceProcess.ServiceController.Start(String[] args) at System.ServiceProcess.ServiceController.Start() at AfconParking.Import.StartService() in E:\ProjectsNet\AfconParking\AfconParking\system\Import.aspx.vb:line 35
The account used for the identity of your ASP.NET application pool ("Network Service" by default) does not have the permissions required to start a service.
To fix this issue, you have a few options:
Re-architect your site to not require interactions between ASP.NET pages and the service control manager. I really can't think of a good reason to require this (the service can simply be started at boot time, and remain running: if the service crashes, you should fix the cause of that, and/or use the corrective actions provided by the SCM. If a service restart is needed to kick of some kind of processing, use an IPC mechanism, such as sockets or named pipes, to communicate between your web app and the service instead).
Create a service account with the appropriate permissions (basically, membership of the local Administrators group) as described in detail here. Do note that this has several security implications, none of them particularly good.
Its a permissions issue, try to run the application pool with an Identity that has permissions to perform service control operations.
Read this kb to find out how to grant user such a permissions:
http://support.microsoft.com/kb/325349
Services have Access Control Lists (like files etc.). By default most normal and restricted user accounts (including the default account used by ASP.NET workers) do not have permissions to control or see the status any services.
You can either set an ACL on the service that allows the IIS worker to control the service, or run the web application with an account that already has rights.
The latter option would probably give the web application a dangerous level of access (e.g. what would happen if a web user found a security vulnerability), but is a quick approach to confirming that it is a service access permission.
Setting an ACL is the better solution, but I don't think there is a UI to set the ACL (except in group policy) which makes things harder. You'll need to use the command line tools (e.g. SUBINACL.exe)

ASP.NET - temporarily impersonate the app pool service ID when default it to impersonate the user?

I have to call some code in a SharePoint site that runs under the same service ID that the web application is running under. By default, SharePoint impersonates the user viewing the web page, and the users don't have the necessary permissions.
What is the best way to run some code using the web application's service ID, then revert back to using the default impersonation?
SPSecurity.RunWithElevatedPrivileges
Nat is right. You should use SPSecurity.RunWithElevatedPrivileges. Under the covers it does a ReverToSelf that Anthony mentions, but it is much easier to use the helper method. You can use an inline delegate as in the following example.
The main thing to realize is that this delegate runs under a separate application domain which basically means that you want to use a SPSite or SPWeb you must re-instantiate them within the delegate as shown below.
SPSecurity.RunWithElevatedPrivileges(delegate()
{
// Your are now inside the delegate
// Anything provided within this block is marshaled across the app domain
using (SPSite site = new SPSite("http://myserver/sites/mysite"))
{
using (SPWeb web= site.OpenWeb())
{
// Do stuff here
}
}
});
Under ASP I had a utility DLL that I could use to call Win32's RevertToSelf() function (found in the advapi32.dll) to get the ASP to run under the identity of the application pool.
Of course once there, there's no going back to the original identity the thread was using but that's not really a problem. Once the current request had ended, the next request would run again under the users identity (or the anonymous users).
You could probably do the same with PInvoke in ASP.NET but I wouldn't know what effect that might have on the framework. I'm certain it would last only for the current request. I don't think there is any standard .NET API to do this.
easy! Wrap the calls you're making in a HostingEnvironment.Impersonate() block.
http://msdn.microsoft.com/en-us/library/system.web.hosting.hostingenvironment.impersonate.aspx

Resources