TL;DR: I need to call CreateProcessWithLogonW from my IIS-hosted ASP.NET application in order to call an external executable under a different account and delegate a job to it. How can I do this?
The reason I can't simply use Process.Start API is that the external executable has a number of COM dependencies that need to be configured. This entails making sure a number of registry entries are there and that the account has required launch and activation permissions which need to be set via DCOM config. Suffice to say this has proven quite unmanageable to do for each of the AppPool identities on every server we have.
The idea is to create a single additional interactive account on each system, configure it as necessary, and then test that the external executable can be successfully run by hand.
The problem now becomes how to start this executable from ASP.NET app and have it run under the new account (just the executable, not the whole ASP.NET app!).
Simply calling CreateProcessWithLogonW results in "Access denied" error, which I've been told is the consequence of the AppPool identity's under-privileged nature.
I've been searching the internet and I've seen a lot of answers on how to grant file-system permissions to AppPool identities. Nothing about permissions for CreateProcessWithLogonW, however, short of changing the identity to something else.
But I'm still wondering if there is a way to configure the AppPool identity for this?
I don't feel comfortable changing the identity to Local Service or Local System as has been suggested elsewhere. I'm worried it could enable unknown additional permissions that are not really needed by the ASP.NET app, which could in turn change the security implications of a rather large code base.
I admit I'm probably having a "Here be dragons!!!" type of reaction to identity changing, but I'm also honestly wondering why I couldn't simply add one additional permission to an existing identity instead?
I have the following setup. There's a Azure web role process (WaIISHost.exe) running elevated and there's IIS7 application pool running under a local user. The local user is being created by the elevated WaIISHost.exe when it first runs. There's also an out-proc COM object registered with DCOM.
I want to be sure that the IIS process will be able to instantiate the COM object. So I need to somehow login under the local user and try to instantiate the COM object.
My current approach is to call LogonUser() (requesting interactive logon) via P/Invoke, obtain a logon token, then start a new thread and from that thread call ImpersonateLoggedOnUser() to switch to the other user and once that is done the other thread tries to instantiate the COM object.
All great, except the interactive logon is only for Administrators by default now so my approach no longer works - I get
Logon failure: the user has not been granted the requested logon type at this computer
yet if I just skip the check the IIS later instantiates the COM object just fine.
So I need to change my validation code.
One option is to try add the required privilege (interactive logon) to the user, but that's lots of P/Invoke code plus there's a chance that at come point Microsoft changes something else and I'm disallowed to adjust privileges altogether.
Is there another way to verify that a COM object can be successfully instantiated from under another user account?
This is not a universal answer, but fine for this specific scenario. One possible solution is to implement a special URL in IIS that will accept local requests only (check for HttpRequestBase.IsLocal) and try to instantiate the COM object. The code that previously tried impersonation will now send an HTTP request to that URL (on the same computer).
I have built an application using C# which accesses the registry and installation folder for read/write information. A normal user having limited privileges is getting an error while accessing/writing the information (in registry or installation folder).
Is there a way in which all types of users are able to run that application smoothly?
It is possible, although it's not completely straightforward.
You're gonna have to impersonate another user (who, in turn, has to have all the required privileges).
Check this question for details: Windows Impersonation from C#
There are two ways, the simple and complex. First - install the program per user rather than per computer. Second - to write a service that will operate under the privileged user and perform the necessary procedures (accessing/writing the information) for your application.
I am using C# and .Net Framework 4.
I am looking for a foolproof method to get the login id of the currently logged in windows user that is not susceptible to impersonation or hacking. I am looking for this in the form of: DOMAINNAME\USERNAME
e.g. SOMEDOMAIN\JohnDoe
Currently the best I have is:
var identity = System.Security.Principal.WindowsIdentity.GetCurrent();
var currentLoginId = identity.Name;
Is this open to impersonation (outside of someone knowing both the username and password) and if so is there a better way of doing this?
There can be at least four different identities involved at this point:
The identity assigned to the thread
The identity assigned to the process
The account of the user who started the process
The account of the user who logged onto the PC
In your code you're getting (1). This is normally fine, and is usually the same as (2).
To retrieve (2), you could:
Call WindowsIdentity.GetCurrent to get the impersonated identity
Undo impersonation by calling the Win32 RevertToSelf function
Look at WindowsIdentity.GetCurrent to get the underlying process identity
Reset the thread identity by impersonating the identity from step (1)
(2) and (3) will be the same unless you've written unmanaged code that changes the process identity. As #Daniel points out, (3) and (4) could legitimately be different in the presence of the Windows "run as" command.
Edit: You can trust that the current WindowsIdentity is who it says it is, insofar as you can trust any given piece of data in your application.
You could spoof it by attaching a debugger to your process and faking the return value from this call, but if you're going to do that, you might as well fake the piece of code where you pass the user name to the database.
The 100% safe way to do this is to use integrated authentication/SSPI to connect to the database.
Only a partial answer:
I believe System.Security.Principal.WindowsIdentity.GetCurrent(); will return the windows user account associaed with the currently executing thread. If the thread or application was started under a different user account from the logged in user, you won't get what you're after using this.
If you can be sure that your aplication was not started using the "run-as" feature (or programatic equialent) and you're not doing anything internally with respect to the thread's identity, you can probably be sure this is the logged in user's account but I'm not 100% on this.
It may be possible to find the user account associated with the widows "session" within which the application is running by using ADSI (see System.DirectoryServices).
Running into a problem where on certain servers we get an error that the directory name is invalid when using Path.GetTempFileName. Further investigation shows that it is trying to write a file to c:\Documents and Setting\computername\aspnet\local settings\temp (found by using Path.GetTempPath). This folder exists so I'm assuming this must be a permissions issue with respect to the asp.net account.
I've been told by some that Path.GetTempFileName should be pointing to C:\Windows\Microsoft.NET\Framework\v2.0.50727\temporaryasp.net files.
I've also been told that this problem may be due to the order in which IIS and .NET where installed on the server. I've done the typical 'aspnet_regiis -i' and checked security on the folders etc. At this point I'm stuck.
Can anyone shed some light on this?
**Update:**Turns out that providing 'IUSR_ComputerName' access to the folder does the trick. Is that the correct procedure? I don't seem to recall doing that in the past, and obviously, want to follow best practices to maintain security. This is, after all, part of a file upload process.
This is probably a combination of impersonation and a mismatch of different authentication methods occurring.
There are many pieces; I'll try to go over them one by one.
Impersonation is a technique to "temporarily" switch the user account under which a thread is running. Essentially, the thread briefly gains the same rights and access -- no more, no less -- as the account that is being impersonated. As soon as the thread is done creating the web page, it "reverts" back to the original account and gets ready for the next call. This technique is used to access resources that only the user logged into your web site has access to. Hold onto the concept for a minute.
Now, by default ASP.NET runs a web site under a local account called ASPNET. Again, by default, only the ASPNET account and members of the Administrators group can write to that folder. Your temporary folder is under that account's purview. This is the second piece of the puzzle.
Impersonation doesn't happen on its own. It needs to be turn on intentionally in your web.config.
<identity impersonate="true" />
If the setting is missing or set to false, your code will execute pure and simply under the ASPNET account mentioned above. Given your error message, I'm positive that you have impersonation=true. There is nothing wrong with that! Impersonation has advantages and disadvantages that go beyond this discussion.
There is one question left: when you use impersonation, which account gets impersonated?
Unless you specify the account in the web.config (full syntax of the identity element here), the account impersonated is the one that the IIS handed over to ASP.NET. And that depends on how the user has authenticated (or not) into the site. That is your third and final piece.
The IUSR_ComputerName account is a low-rights account created by IIS. By default, this account is the account under which a web call runs if the user could not be authenticated. That is, the user comes in as an "anonymous".
In summary, this is what is happening to you:
Your user is trying to access the web site, and IIS could not authenticate the person for some reason. Because Anonymous access is ON, (or you would not see IUSRComputerName accessing the temp folder), IIS allows the user in anyway, but as a generic user. Your ASP.NET code runs and impersonates this generic IUSR___ComputerName "guest" account; only now the code doesn't have access to the things that the ASPNET account had access to, including its own temporary folder.
Granting IUSR_ComputerName WRITE access to the folder makes your symptoms go away.
But that just the symptoms. You need to review why is the person coming as "Anonymous/Guest"?
There are two likely scenarios:
a) You intended to use IIS for authentication, but the authentication settings in IIS for some of your servers are wrong.
In that case, you need to disable Anonymous access on those servers so that the usual authentication mechanisms take place. Note that you might still need to grant to your users access to that temporary folder, or use another folder instead, one to which your users already have access.
I have worked with this scenario many times, and quite frankly it gives you less headaches to forgo the Temp folder; create a dedicated folder in the server, set the proper permissions, and set its location in web.config.
b) You didn't want to authenticate people anyway, or you wanted to use ASP.NET Forms Authentication (which uses IIS's Anonymous access to bypass checks in IIS and lets ASP.NET handle the authentication directly)
This case is a bit more complicated.
You should go to IIS and disable all forms of authentication other than "Anonymous Access". Note that you can't do that in the developer's box, because the debugger needs Integrated Authentication to be enabled. So your debugging box will behave a bit different than the real server; just be aware of that.
Then, you need to decide whether you should turn impersonation OFF, or conversely, to specify the account to impersonate in the web.config. Do the first if your web server doesn't need outside resources (like a database). Do the latter if your web site does need to run under an account that has access to a database (or some other outside resource).
You have two more alternatives to specify the account to impersonate. One, you could go to IIS and change the "anonymous" account to be one with access to the resource instead of the one IIS manages for you. The second alternative is to stash the account and password encrypted in the registry. That step is a bit complicated and also goes beyond the scope of this discussion.
Good luck!
I encountered this error while diagnosing a console app that was writing in temp files. In one of my test iterations I purged all the files/directories in temp for a 'clean-slate' run. I resolved this self inflicted issue by logging out and back in again.
Could be because IIS_WPG does not have access to a temp folder. If you think it is a permission issue, run a Procmon on asp.net worker process and check for AccessDenied errors.
I was having the same problem with one of my ASP.Net applications. I was getting Path.GetTempPath() but it was throwing an exception of:
"Could not write to file "C:\Windows\Temp\somefilename", exception: Access to the path "C:\Windows\Temp\somefilename" is denied."
I tried a few suggestions on this page, but nothing helped.
In the end, I went onto the web server (IIS server) and changed permissions on the server's "C:\Windows\Temp" directory to give the "Everyone" user full read-write permissions.
And then, finally, the exception went away, and my users could download files from the application. Phew!
You can use Path.GetTempPath() to find out which directory to which it's trying to write.