I have a website that is attempting to retrieve documents from a folder that's restricted to specific AD groups/users. Given a user that's authenticated using Windows Authentication, I wish to attempt to check if they have access to a specific location and display/hide documents as needed. I was under the impression that using Windows Authentication and ASP.NET Impersonation would use the current user's credentials when executing a request, rather than the Application Pool's credentials, but this doesn't seem to be the case because I'm an admin capable of opening the restricted folders in Windows Explorer, but I can't read them from my application.
Is there any way to use the current requesting user's credentials to access files on the local filesystem?
In my web.config:
<appSettings>
<add key="documentFolder" value="C:\Users\auser\source\repos\myapp\myapp\docs\folder1,C:\Users\auser\source\repos\myapp\myapp\docs\folder2,C:\Users\auser\source\repos\myapp\myapp\docs\secured" />
<add key="securityFolder" value="C:\Users\auser\source\repos\myapp\myapp\docs\secured"/>
</appSettings>
<system.web>
<authentication mode="Windows" />
<identity impersonate="true"/>
<authorization>
<deny users="?" />
</authorization>
</system.web>
I'm attempting to access files in two places. First, I index them (for now this runs from Application_Start):
var dirs = ConfigurationManager.AppSettings["documentFolder"].ToString().Split(',');
foreach (var dir in dirs)
{
foreach (var file in Directory.GetFiles(dir, "*.pdf", SearchOption.TopDirectoryOnly))
{
try
{
FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read); // <-- Fails with "Access to the path 'C:\Users\auser\source\repos\myapp\myapp\docs\secured' is denied."
}
catch (Exception ex)
{
logger.Error("Failed to read file.", ex);
}
}
}
At a later point I attempt to check if the user has access:
protected void Button1_Click(object sender, EventArgs e)
{
bool canSeeSecurityDocuments;
try
{
System.Security.AccessControl.DirectorySecurity ds = Directory.GetAccessControl(ConfigurationManager.AppSettings["securityFolder"].ToString()); <-- Fails with "Attempted to perform an unauthorized operation."
canSeeSecurityDocuments = true;
}
catch (Exception ex)
{
canSeeSecurityDocuments = false;
}
}
From procmon:
Desired Access: Read Attributes, Read Control
Disposition: Open
Options: Open Reparse Point
Attributes: n/a
ShareMode: Read, Write, Delete
AllocationSize: n/a
Impersonating: mydomain\myuserid <- redacted, but correct.
Based off research about integrated vs classic pipelines, I moved the GetFiles
call to a different location and wrapped it in a WindowsImpersonationContext
to see if that'd force impersonation, which it didn't. (From this SO answer):
var current = System.Security.Principal.WindowsIdentity.GetCurrent();
logger.Debug(current.Name); <-- This is my identity
WindowsIdentity clientId = (WindowsIdentity)User.Identity;
using (WindowsImpersonationContext wic = clientId.Impersonate())
{
current = System.Security.Principal.WindowsIdentity.GetCurrent();
logger.Debug(current.Name); <-- This is still my identity
// call to GetFiles, as above. Still fails.
}
current = System.Security.Principal.WindowsIdentity.GetCurrent();
logger.Debug(current.Name); <-- still me. Nothing seems to change.