Integrated Windows Authentication breaks File.Exists - c#

I have run into the wierdest issue, and I'm hoping some ASP.NET expert can give me some clues as to what is going on.
I have an ASP.NET Website running on IIS 6.0. On it, I have an .asmx webservice. In the .asmx webservice, I use System.IO.File.Exists() to check if a file exists on a network share. The site uses Impersonation.
Here's the massive kicker: If I set IIS to run the site using Basic Authentication, the File.Exists line returns True. If I set it to Integrated Windows Authentication, it returns False. I change nothing else, i can change back and forth and the exact same thing happens. I'm logging some information before the call to File.Exists() and indeed, both User.Identity.Name and System.Security.Principal.WindowsIdentity.GetCurrent().Name return the exact same name regardless of setting (my own account which obviously has access to the files I'm looking for).
I would post things I've tried but honestly, what can you try in this situation, where one setting which shouldn't affect the issue is the problem. I have tried turning impersonation off, and that also makes the File.Exists() call return True (which is blowing my mind since my own user has far higher permissions than the account running the appPool).
Any ideas what I can look into to get this issue resolved?

OK what is happening is that the server is not able to delegate the authentication. That is, it cannot act on behalf of the user to another server, only to itself.
Delegation is not a problem for Basic authentication because the server knows your password (because you told it as part of the basic authentication), so it can perform authentication to the remote computer.
If you are in an Active Directory/Kerberos environment, you must configure the server to be trusted for delegation. I have never had to do this so I am not sure exactly how, but it is a matter of configuration, not programming. You may have to configure the Server machine account, or the IIS identity or both, depending on your configuration.
See here for some references:
http://technet.microsoft.com/en-us/library/cc739474(v=WS.10).aspx
If you are not in a Kerberos environment, you must either use basic authentication, or get the password from the user in some other way, and call LogonUser yourself to impersonate the user. If Basic authentication is used or a clear-text password is used in any other way, you must of course use HTTPS/TLS for security.

Related

Impersonate user to access file on remote server - Access denied

I have an MVC web application that is supposed to allow users to download files that are stored as UNC paths in a database. These files can be in any number of locations on remote servers/shares.
E.g. Server 1 hosts the web application that is used to download a file stored on Server 2
I do not want to give permissions to these folders to the hosting service account, as the security should be dependent on what the user has access to. Therefore, I'm attempting to use Impersonation to retrieve the file.
When I debug on my local machine, everything works great. It impersonates my user and downloads the file.
When I deploy to my test server, I'm getting the following error:
Access to the path '\\Server2\SharedFolder\somefile.txt' is denied
I've tried various pieces from this Microsoft link, but am not having much luck.
Scenarios I've tried:
Just giving the permission to the service account of the AppPool works fine, but as I said, isn't ideal
Implementing the Impersonate a Specific User in Code from the above article, which works perfectly with a hard-coded user and password. This situation is also not ideal.
Implementing the Impersonate the Authenticating User in Code from the above article. This seems to be exactly what I need, but this is what generates the Access Denied error.
The Code that I want to work:
System.Security.Principal.WindowsImpersonationContext impersonationContext;
impersonationContext =
((System.Security.Principal.WindowsIdentity)User.Identity).Impersonate();
//Code to read all bytes from the file path
impersonationContext.Undo();
I have logging, and System.Security.Principal.WindowsIdentity.GetCurrent().Name after the impersonation does return the intended user (my account instead of the service account), so it does appear to be working.
I thought maybe it was a double-hop thing, so I have also added SPNs for the server and the service account, making sure their Delegation in AD was set to allow for any service. That hasn't helped either.
This question seems to have the exact same problem as me, but there's no follow-up on what the final solution was. I did try the Process Monitor part, but it didn't help at all.
I'm at a loss to why Impersonation seems to be working, but I'm denied access to a file on a second server.
Update 1
I've played around more with my IIS settings and trying to get Kerberos properly set up. The only thing enabled in my IIS Authentication is "Windows Authentication".
When I spit out details after my Impersonate() call, I'm finding that ImpersonationLevel = Impersonation
Is that how it should be, or should that be returning Delegation ?
It would seem the issue was mostly due to my setup with Kerberos and SPNs. I undid all my settings and re-registered my service account, and the Impersonation ended up working properly.
The issue now is that it only seems to work with Internet Explorer. Chrome and MobileIron are doing something different that prevents the ImpersonationLevel of Delegation. That's a whole other question...

Granting ASP.NET access to download file from sharepoint website

I have an ASP.NET application which I am testing on the ASP.NET Development server. The application is meant to download a file from an intranet sharepoint site. I am using WebClient.DownloadFile() for this purpose. But I am getting the following exception-
"The remote server returned an error: (401) Unauthorized."
The account with which I am logged into Windows while running the application has access to the resource which I want to download.
I tried ro run the application on IIS 7.5 as well, giving the application pool the required identity to download the file. But still got the same error.
Any idea what exactly is the problem and how to overcome it? Thanks in advance!
It's a problem with double hop delegation. Look at this: http://weblogs.asp.net/owscott/archive/2008/08/22/iis-windows-authentication-and-the-double-hop-issue.aspx
Assuming normal Windows (NTLM) authentication on SharePoint site.
Most likley you are hitting by design behavior "double hop delegation" sometimes called "one hop NTLM hell" - credentials passed from client (browser) to server (your ASP.Net application) can't be used outside of the box. As result your ASP.Net application autorizes as more or less anonymous user and can't authorize on SharePoint site.
Common way to solve it - have some sort of trusted account and run your site's code under that account. Than when accessing the site you run code under process' account (not impersonating the user). You'll need to review code to have reasonable security measures as bugs will allow anyone use your site to access SharePoint/other resources under that special account.
Problem got resolved by setting UseDefaultCredentials property of the WebClient to true
This is a Kerberos issue, as Kirill mentioned. Before you start setting SPNs and the like, please follow the advice on this link first.
I recently had a frustrating week of trying to get Kerberos to work on a locked down domain and at the end of it all I found all I had to do was set useAppPoolCredentials to true.
If that doesn't help, this link provided quite useful to me as well.
Good luck!

Can AD be used over the internet to authenticate a user?

Right now we have AD/Exchange to manage all of our users logins/e-mail on-site at the office. The major app that everyone uses maintains its own login accounts and all users have a tendency to forget login information for at least one of the two logins.
What I'm considering doing is using AD to authenticate the user in the application so that they don't even have to login to the app after they've logged into their machine.
The problem is that there are small number of users that work off-site (the app can work over the internet) and just use the machine's local account (which is causing problems of its own).
What I'm wondering is, will using AD to authenticate users on-site still be an option if a user works off-site?
The answer to almost any question posed to a programmer is "Yes..." It's what comes after the ellipses that is important. You may not want to do the things that come after the ellipses.
Based on the information in your question I think the answer is "No" but there are several scenarios where we could change that to a "yes".
If the AD account is only being used to authenticate that a user knows the password, then you could make a web service, host it in your domain, set it up to use windows authentication and SSL, modify the application to prompt the user for credentials, and call a method in the web service using those credentials. In that scenario, a successful call to the web service means that the user is authenticated. You could use the user's credentials to continue from there.
Detecting weather the application needs to prompt the user for credentials or not could be done by attempting to call the web service with the user's logged in credentials first. If this call fails then you know you need to prompt the user.
Not knowing the rest of the details of your application however means that there are many scenarios where this would not be enough.
I have done something very similar to what I described above. My scenario was the reverse: the application worked over the internet but I wanted it to be easier to log in in the cases where the machine has domain membership.
As an aside, the members who work from home: are they using laptops that are part of the domain or are they using machines that are not connected? In this case you may be able to use cached credentials but you should ask that question over at ServerFault.
Yes, you can definitely do that. It'll be a bit of work though.
What your app would have to do is either find out automagically whether it's directly connected to the office LAN, or working away from the office. Or you could have the user tell you, of course :-)
If it's on the LAN, no problem - you authenticate against AD.
If it's away from the office, you could e.g. call a WCF service on the company LAN, pass your Windows credentials, and have it authenticate you against the company AD. If you provide the right set of credentials, you'll be authenticated and allowed to work - if you're not allowed to log in, the call to the WCF service would fail.
You could do this almost automatically by using Windows credentials - in which case the "remote" user would still have to log on to your domain and use his / her normal Windows credentials; or you can pass username/password over the wire to WCF, or even install a certificate on the remote user's machine that WCF will then map to an AD account on the server side.
The options are plentiful! :-)
Marc

Why can't I open a file using impersonation on a remote machine?

I have a WCF service written in C# being hosted on a remote machine, running as the local administrator account. From my machine logged in as an active directory user, I am sending a command that simply tells it to open a file on the network. I have access to the file, but the administrator account on the host machine does not. I'm using the [OperationBehavior(Impersonation=ImpersonationOption.Required)] meta tag on the method that requires impersonation, and I have the credential type and security modes set correctly. I can verify that account is indeed trying to be impersonated by comparing Windows Identities, but I still get an access denied exception. I'm thinking it has something to do with active directory not authenticating the impersonated user. Is there something I'm missing?
You are entering the domain of Kerberos security and two hops-authentication.
You have two options:
Take the red pill: try to get the two hops-authentication to work. Make sure you have at least a Windows Server 2003 domain, time properly synchronized between all machines and setup proper delegation for the spefic users/computer accounts. If you're really "lucky" you'll have to configure SPNs with SetSPN.
Take the blue pill: forget two hops-authentication, impersonate the WCF service under an account that has just enough rights, and check authorization in an earlier step.
Forgive my frustration, but I do think that my brief experience with this topic has cost me at least 10 years of my life. I hate to see that happend to anyone else. Anyways, this post should give you enough Google keywords if you're feeling brave.
Eventlog and network monitor are useful for debugging...
You also probably need to set up delegation from the web server to the file server. This will allow the file server to trust credentials that the web server has validated. See this MSDN article on how to set up delegation for your application, particularly the section on configuring AD.

Path.GetTempFileName -- Directory name is invalid

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.

Categories