How to Impersonate windows users UNC Paths via web server?
Question asked by Greg - July 16 at 6:59 AM
Answered
I have a MVC web application that works great with the File Ultimate plugin. I followed the guide on your main site and everything works wonderful. I deployed my code to our web server and I get an error that states: "(5) Access is denied". 

After doing some digging, this is due to the web servers IIS attempting to access a UNC path that it does not have permission to, thus failing. I tried updating my applications web.config to the below using my personal username/password and it worked.

<location path="filemanager.ashx">
    <system.web>
      <identity impersonate="true" userName="JohnDoe" password="password123" />
    </system.web>
  </location>
Unfortunately, I don't want to use my personal username/password OR any hardcoded username/password for this website. I want the windows authenticated user who is logged in the website to use their account to access the UNC path. I tried removing username/password hoping it would grab the users login information, but it did not. You still get the "(5) Access is denied" with the below entry in your web.config.

How do I successfully get a user to access a UNC path via a web server and not get "(5) Access is denied"?

<location path="filemanager.ashx">
    <system.web>
      <identity impersonate="true" />
    </system.web>
  </location>


*UPDATE*
I saw another post about setting User Authentication = True in the code. 

Location = "Path=SOMEPATH; Authenticated User=true"
I tried the following, but still no luck. Any thoughts on what I could be doing wrong?
var fileManager = new FileManager
        {
            Width = 800,
            Height = 600,
            Resizable = true,
            ShowFoldersPane = false,
            ShowRibbon = false,
            ViewLayout = ViewLayout.Details
        };

        var rootFolder = new FileManagerRootFolder
        {
            Name = "Root",
            Location = "Path=\\abcs8080bbb01-cifs\directory\folder; Authenticated User=true"

        };

        rootFolder.AccessControls.Add(new FileManagerAccessControl
        {
            Path = @"\",
            AllowedPermissions = FileManagerPermissions.Full
        });

        fileManager.RootFolders.Add(rootFolder);

        <head>
            <title>File Manager</title>
            @this.RenderHead(fileManager)
        </head>
        <body>
            @this.RenderBody(fileManager)
        </body>
*Update 2*

I did more digging and found some pretty good documentation on the Location Property. 

rootFolder.Location = @"Path=\\server\share; Authenticated User=Windows";

Found exactly what I was looking for, but I still cannot get the shared drive to display the files! I updated my code block to what I'm trying. What am I doing wrong?


var fileManager = new FileManager
        {
            Width = 800,
            Height = 600,
            Resizable = true,
            ShowFoldersPane = false,
            ShowRibbon = false,
            ViewLayout = ViewLayout.Details
        };

        var rootFolder = new FileManagerRootFolder();

        rootFolder.Name = "Root";
        rootFolder.Location = @"Path=\\njtclda01-server\directory\example; Authenticated User=Windows";

        rootFolder.AccessControls.Add(new FileManagerAccessControl
        {
            Path = @"\",
            AllowedPermissions = FileManagerPermissions.Full
        });

        fileManager.RootFolders.Add(rootFolder);

        <head>
            <title>File Manager</title>
            @this.RenderHead(fileManager)
        </head>
        <body>
            @this.RenderBody(fileManager)
        </body>

5 Replies

Reply to Thread
0
Cem Alacayir Replied
Employee Post Marked As Answer
Hi,
When using one of the following:

rootFolder.Location = @"Path=\\server\share; User Name=USERNAME; Password=PASSWORD";
rootFolder.Location = @"Path=\\server\share; Authenticated User=Windows";
You should not enable impersonation in Web.config (do not put <identity impersonate="true" /> tag or set it to false). The impersonation will be done by FileUltimate when necessary automatically (whenever accessing the file system). If you enable it in Web.config, it would mean double impersonation which will cause access problems.

The below information will also be useful if you experience problems when accessing your created root folder with UNC paths:
(Also see “Delegation” section for more details in this article: https://msdn.microsoft.com/en-us/library/ff647404.aspx?f=255&MSPPError=-2147217396)

There are 2 impersonation levels Impersonation and Delegation and Windows does not allow accessing files in IIS if the level is not Delegation.
So with IIS Windows Authentication, impersonation is not enough network resources so you also need delegation:

Impersonation only allows you to access resources local to the web server as the browser user. If you want that user's identity to travel across the then you need delegation.

The reason it works when you provide user name and password is that when Windows has the password, it can access network resources.
However when there is no password, IIS creates a user token which can only access local resources.
IIS only creates a delegation user token when you use Basic Authentication because then it knows your password (passed in clear text).
So possibly you will see that you can access the path when you enable Basic Authentication. However this is not desirable because IIS would ask your credentials
every time you access the site (no automatic logon I guess). So we should focus on enabling delegation for Windows Authentication (your current setting)

To enable delegation in IIS, you need to enable Negotiate:Kerberos provider:

Under "Windows Authentication" right click and select "Providers". Add "Negotiate:Kerberos" and move it to the top

Note that configuring delegation seems to be a pain. So there are some other steps that may be required.
However we should first start by doing the above.

The other steps include
-    Configuring the user's account to allow delegation in AD
-    Setting up SPNs for users

One user solved the problem like this:

Eventually we found out what the problem was with impersonation level.

When the client negotiated with the server the authentication, a Kerberos ticket was requested but, as we used an alias for accessing the intranet, the ticket was not delivered, so the authentication was NTLM, not Kerberos.
The impersonation level was “Impersonate” not “delegate” due to NTLM cannot impersonate at delegation level (required for accessing to network resources). The result was the error denying the access.

To solve it, we had to say that the address (intranet.server.com) is a principal one at the web server (manweb001).
So, in the end, solving it was as easy as typing, at the web server:

Setspn -A HTTP/intranet.server.com manweb001

0
Greg Replied
Thank you for the detailed response this is very helpful. I started off by enabling Negotiate:Kerberos provider and still no luck. I guess I need to try the other two steps which are "Configuring the user's account to allow delegation in AD" and "Setting up SPNs for users". I saw the note below about the "Setspn -A HTTP/intranet.server.com manweb001" command. Could not get that to work either, but I believe that is a permission issue on my end. Do you have any examples for the Configuring the user's account to allow delegation in AD ?
0
Greg Replied
Update

1) Under "Windows Authentication" right click and select "Providers". Add "Negotiate:Kerberos" and move it to the top - COMPLETE
2) Ran the Service Principal Name (Setspn) command with my apps information etc. - COMPLETE

With #1 and #2 complete, I'm still getting the same issue.

3) "Configuring the user's account to allow delegation in AD" - I'm still trying to figure out how to do this one. Are there any links or documentation on how to do this?

Thanks,
Greg

0
Cem Alacayir Replied
Employee Post
Hi Greg,
Were you able to solve the issue?

For future reference, we have released a new version which includes some features to troubleshoot permission issues more easily.

Version 7.0.5 - August 6, 2019
  • Improved: Detailed "Access Denied" error messages will be shown to troubleshoot insufficient permission issues.The error message will include the safe display path and current windows identity.

  • Added: DocumentViewer.DebugMode property for displaying detailed error messages for troubleshooting.Exceptions will be displayed with stack trace, inner exceptions and data. The details will be shown in a textbox on the error dialog when this property is set to true. This property should not be set to true permanently on production as the exception details can reveal security sensitive information like server file paths.

I also prepared a debug/test script for you. You can replace the yellow highlighted path value with your path and test the results.

ImpersonationTest.cshtml (ASP.NET MVC):
@using System.Security.Principal
@using System.Threading
@using GleamTech.Security
<!DOCTYPE html>

<html>
<head>
    <title>Impersonation Test</title>
</head>
<body>

<p>
    WindowsIdentity.GetCurrent().Name:<b>[@WindowsIdentity.GetCurrent().Name]</b><br/>
    Thread.CurrentPrincipal.Identity.Name:<b>[@Thread.CurrentPrincipal.Identity.Name]</b><br/>
    HttpContext.Current.User.Identity.Name:<b>[@HttpContext.Current.User.Identity.Name]</b><br/>
    Request.LogonUserIdentity.Name:<b>[@Request.LogonUserIdentity.Name]</b><br/>
    Request.LogonUserIdentity.IsAuthenticated:<b>[@Request.LogonUserIdentity.IsAuthenticated]</b><br/>
    Request.LogonUserIdentity.ImpersonationLevel:<b>[@Request.LogonUserIdentity.ImpersonationLevel]</b><br/>
</p>
<p>
    @{
        const string path = @"\\server\share";

        @:Trying to access <b>[@path]</b> with impersonation...<br/>

        var impersonationInfo = new ImpersonationInfo(AuthenticatedUser.Windows);
        using (var impersonationContext = impersonationInfo.Impersonate())
        {
            if (impersonationContext == null)
            {
                @:|--> impersonationContext is null, so no impersonation is happening<br/>
            }
            else
            {
                var current = WindowsIdentity.GetCurrent();
                @:|--> Impersonated User:<b>[@current.Name]</b><br/>
                @:|--> Impersonation Level:<b>[@current.ImpersonationLevel]</b><br/>
            }
            try
            {
                var directoryInfo = new DirectoryInfo(path);
                directoryInfo.EnumerateFileSystemInfos().Any();
                @:|--> Path access: <b>SUCCESS</b><br/>
            }
            catch(Exception exception)
            {
                @:|--> Path access: <b>ERROR:</b> <pre style="margin: 0 0 0 20px;">@exception.ToString()</pre>
            }
        }
    }
</p>

</body>
</html>

ImpersonationTest.aspx (ASP.NET WebForms):
<%@ Page Language="C#" AutoEventWireup="true" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Security.Principal" %>
<%@ Import Namespace="System.Threading" %>
<%@ Import Namespace="GleamTech.Security" %>

<!DOCTYPE html>

<html>
<head runat="server">
    <title>Impersonation Test</title>
</head>
<body>

<p>
    WindowsIdentity.GetCurrent().Name:<b>[<%=WindowsIdentity.GetCurrent().Name%>]</b><br/>
    Thread.CurrentPrincipal.Identity.Name:<b>[<%=Thread.CurrentPrincipal.Identity.Name%>]</b><br/>
    HttpContext.Current.User.Identity.Name:<b>[<%=HttpContext.Current.User.Identity.Name%>]</b><br/>
    Request.LogonUserIdentity.Name:<b>[<%=Request.LogonUserIdentity.Name%>]</b><br/>
    Request.LogonUserIdentity.IsAuthenticated:<b>[<%=Request.LogonUserIdentity.IsAuthenticated%>]</b><br/>
    Request.LogonUserIdentity.ImpersonationLevel:<b>[<%=Request.LogonUserIdentity.ImpersonationLevel%>]</b><br/>
</p>
<p>
<%
    const string path = @"\\server\share";
%>
Trying to access <b>[<%=path%>]</b> with impersonation...<br/>
<%
    var impersonationInfo = new ImpersonationInfo(AuthenticatedUser.Windows);
    using (var impersonationContext = impersonationInfo.Impersonate())
    {
        if (impersonationContext == null)
        {
            %>
            |--> impersonationContext is null, so no impersonation is happening<br/>
            <%
        }
        else
        {
            var current = WindowsIdentity.GetCurrent();
            %>
            |--> Impersonated User:<b>[<%=current.Name%>]</b><br/>
            |--> Impersonation Level:<b>[<%=current.ImpersonationLevel%>]</b><br/>
            <%
        }
        try
        {
            var directoryInfo = new DirectoryInfo(path);
            directoryInfo.EnumerateFileSystemInfos().Any();
            %>
            |--> Path access: <b>SUCCESS</b><br/>
            <%
        }
        catch(Exception exception)
        {
            %>
            |--> Path access: <b>ERROR:</b> <pre style="margin: 0 0 0 20px;"><%=exception.ToString()%></pre>
            <%
        }
    }
%>
</p>

</body>
</html>

0
Greg Replied
Hello,

I worked on this for over a week doing all sorts of tests and could not get the documents to show up when I deployed to our remote DEV web server. I had our IT team configuring the user's account to allow delegation in AD and still nothing. 
I saw your note about a new version. I went ahead and updated to the latest version and ran your test code. I attached a screenshot of what I saw. At this point I'm stumped and don't really know what else to do. I feel like I've tried every solution but still can't get this to work on a remote server.


NOTE: It seems your image upload is not working. I'll try and upload it later today.

Error states:

|--> Path access: ERROR:
System.UnauthorizedAccessException: Access to the path '\\npnfs-cifs\PP_A_Business_Intelligence\Exempts' is denied.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.FileSystemEnumerableIterator`1.CommonInit()
   at System.IO.FileSystemEnumerableIterator`1..ctor(String path, String originalUserPath, String searchPattern, SearchOption searchOption, SearchResultHandler`1 resultHandler, Boolean checkHost)
   at System.IO.DirectoryInfo.EnumerateFileSystemInfos()
   at ASP._Page_Views_WFMThrowAway_Index_cshtml.Execute() in C:\inetpub\wwwroot\Collectelligence\Views\WFMThrowAway\Index.cshtml:line 43


Thanks,
Greg

Reply to Thread