How to use impersonation to access a network resource from IIS? - c#

I have a WebApi app that needs to access a network share on a different machine and open Crystal Reports files.
I'm using LogonUser and WindowsIdentity.Impersonate to impersonate a user that has rights to the network share using this code (not complete):
SafeTokenHandle safeTokenHandle;
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
bool returnValue = LogonUser(userName, domainName, userPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out safeTokenHandle);
if (returnValue == false)
{
int ret = Marshal.GetLastWin32Error();
throw new System.ComponentModel.Win32Exception(ret);
}
using (safeTokenHandle)
using (var newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle()))
using (var impersonatedUser = newId.Impersonate())
{
actionToExecute();
}
This works fine when, for example, listing folders or deleting files using managed code (System.IO). However I need to open Crystal Reports files (I have the latest version), and when doing so, I get an exception:
Access denied
I am assuming that CR is trying to load the file within the context of the Application Pool user.
I would probably get this working if I change the Application Pool user to a domain user with enough rights on the network share. But I want to avoid this solution.
Would using the new WindowsIdentity.RunImpersonated for .NET4.6 help or will it have the same outcome. If yes, is there a way to get CR to run within the supplied user context instead of the Application Pool / AppDomain user?
UPDATE
I've had partial success by changing the LogonUser parameters to LOGON32_LOGON_NEW_CREDENTIALS and LOGON32_PROVIDER_WINNT50. Then I tested the following:
Started the project locally from within VS2017 (so that the WebApi project runs within the local IIS Express context) and accessed the network resource using the client app on my machine. This test was a success.
Published the WebApi project on IIS on a separate machine. Started the client (ClickOnce) project on my machine and accessed the same network resource. This test fails.
Same as (2) but the client is published and installed on another machine, and accessing it using terminal server. This test fails.
Why does the first test succeeds but tests 2 and 3 fail? What should the correct logon parameters be if the supplied ones won't always work?

The problem is that ReportDocument doesn't have something like LoadImpersonate, so, it will always try to open the .rpt file using the app-pool permissions.
What you need is delegation, not impersonation, if you are using windows authentication and active directory you need to authorize your server and/or your app-pool account for delegation...
https://support.microsoft.com/en-us/help/810572/how-to-configure-an-asp-net-application-for-a-delegation-scenario
That way the app-pool account will use the user's privileges to access the resources
If that is not posible, as a workaround you can use impersonation to copy the .rpt file to an accessible location for your app-pool account and delete it after use it...

Is it possible for you to use the built in impersonation in ASP.NET?.
https://support.microsoft.com/en-ie/help/306158/how-to-implement-impersonation-in-an-asp-net-application

Related

Impersonation works with local shared file, but does not work with remote one

I have a .net/c# web app (web api) with windows authentication. The service is hosted on my local computer, IIS 10. Application pool identity set to me, currently logged in windows user. Computer is in active directory domain.
I want to access shared file using account, currently logged in to the app. File has appropriate permissions. For this purposes I use impersonation like this:
if (HttpContext.Current.User.Identity is WindowsIdentity windowsIdentity)
{
using (windowsIdentity.Impersonate())
{
FileStream stream = new FileStream(#"\\server\share\file.ext", FileMode.Open, FileAccess.Read);
}
}
I logging in with current windows account, the same as set in app pool identity. This works fine with a shared file on a local computer, where the app is hosted. But does not work with a remote shared file, located on another computer. The other computer is in active directory domain too.
From a hosting computer I can access shared file using windows explorer or my browser. Also if I do not impersonate user, .net trying to access shared file with application pool identity account(set to the same user, me) and it succeeded for both, local and remote files.
It also works with impersonated identity got from LogonUser method from advapi32.dll. But it requires user password and I do not want to request password from user, already logged in to app.
What am i doing wrong?
Update: If a shared file located on hosting machine, then logon event generated by windows (security tab in event viewer) shows the right user. If a shared file located on another machine, then logon event generated by windows on this machine shows the anonymous user. So, account somehow lost.
Update 2: Impersonation works if I run site on IIS like localhost(localhost in url). But if I run it using ip or site name it stops working.
Update 3: Wireshark shows the request for getting ticket(to access shared file server) for delegation fails with error "KRB5KDC_ERR_BADOPTION NT Status: STATUS_NOT_FOUND". Delegation for application pool user allowed in AD.
The same ticket(for cifs/fileshareservername) without delegation can be successfully retrieved(wireshark shows) when doing Dir command in cmd. Seems like problem in AD.
Can't for sure if what you're doing is wrong, but I can tell you what I've done to do a very similar thing. My .Net site doesn't have WindowsLogin normally, so I had to make an extra jump that I think you could do to facilitate the same thing, just perhaps not the best answer.
At login ( in my membershipProvider ) I run this code:
try
{
if (LogonUser(user,domain,password, [AD_LOGIN],
LOGON32_PROVIDER_DEFAULT, ref handle))
{
IntPtr tokenDuplicate = IntPtr.Zero;
if (DuplicateToken(handle, SecurityImpersonation,
ref tokenDuplicate) != 0)
{
// store off duplicate token here
}
}
}
finally
{
if (handle != IntPtr.Zero)
{
CloseHandle(handle);
}
}
then when you need to impersonate, do this:
var context = WindowsIdentity.Impersonate(tokenDuplicate);
try
{
// do your file access here
}
finally
{
context.Dispose();
}
I had to do some funny conversion of that tokenDuplicate variable. It's an integer value but pointing at a specific memory address where the token information is stored. It stays good as long as your logged in.
Why you can't do the impersonate directly on your identity, don't know. I just know it worked for me with a token, and that was my method to get a token I could use for impersonation.
It started working for me with the following settings.
IIS:
Application pool identity set to a specific user(let's say IISUser).
Windows authentication enabled for IIS site. Kernel mode enabled (important!).
All other magic is happening in Active directory:
Computer with shared files has an SPN: cifs/%computer_name%.
Hosting computer(where IIS installed) is trusted for delegation. Delegation tab -> Trust this computer for delegation to specified services only -> Use any authentication protocol. Then select SPN from item 1. Important: you should select computer SPN, not IISUser SPN.
IISUser is trusted for delegation for SPN from item 1.

Accessing Network Path in Windows Service C#

I have developed a windows service using local system as Account. I have used
network path of file
FileInfo fi = new FileInfo(#"\\epm-server\penDocuments_LabMeds\" + Convert.ToString(dr["mrn"]) + "\\SyncedXML\\" + Convert.ToString(dr["xmlfile"]));
if (!fi.Exists)
boolFileNotFound = true;
A dynamic path of a file that is built from database.
It works fine when I run Windows Service in Debug Mode, but when I install it then fileNotExists returns TRUE always like the file doesnt exist but infact it does exist.
This is bugging me a lot now. Kindly help me why its not working. Its a server path. Its getting opened in my PC.
Thanks
Did you notice the double backslashes in front and after SyncedXML (\\SyncedXML\\)?
This is probably the cause of your error.
Additionally I'd use string.Format in such cases to reduce the inadvertently addition of unwanted characters:
var path = string.Format(#"\\epm-server\penDocuments_LabMeds\{0}\SyncedXML\{1}", dr[mrn], dr[xmlfile]);
var fi = new FileInfo(path);
Edit:
If it's permission-related, then it's very likely that your local system account (in whose context the service is running) isn't allowed to access the epm-server.
The path is accessible if you're opening it directly or if you're running the service in debug mode as this is happening in your user context (e.g. YOURDOMAIN\vickyshazad), and you're allowed to access the ressource, whereas NT AUTHORITY\SYSTEM is not.
It's usually a good practise to have a special service account for your developed windows service and grant this user only and exactly the required permissions (least privilege). Maybe ask your system administrator for a service user.
Local System (NT AUTHORITY\SYSTEM) is a highly privileged account that's not recommended to use in general (see MSDN).
Most services do not need such a high privilege level. If your service does not need these privileges, and it is not an interactive service, consider using the LocalService account or the NetworkService account.

Giving Windows Service rights to move files to a remote directory that req. user/pass

I'm using EWS to grab file attachments from emails in an inbox, and need to put those files (if they meet certain criteria) onto a network directory path that requires an active directory user/pass that is not the same as what the machine running the service is using.
There's probably multiple ways to attack this. Without having to set that directory path to allow the user/pass that is running the windows service to have rights to read/write is there a way in code that I can set the user/pass before I try and place the files in that path?
In the installer setup of the windows service I've tried the following:
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.User;
this.serviceProcessInstaller1.Password = "password";
this.serviceProcessInstaller1.Username = #"\\serverName\user";
when I try and install I get an error about mapping the user pass, so I tried this:
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.NetworkService;
this.serviceProcessInstaller1.Password = "password";
this.serviceProcessInstaller1.Username = #"\\serverName\user";
the installer works, the service shows up and I can start it, but when I debug/attach to the process it throws an exception when trying to write to the directory about access rights.
So maybe I'm not even attacking the right issue/section, as this is probably an active directory issue and something not done in code.
Any suggestions?
What you tried there is irrelevant to your problem.
If you're on windows 7, you may workaround by going to [Control Panel]->[User Accounts]->[Credential Manager] to store login information of target machines.

a required privilege is not held by the client while uploading a temporary registry (C#)

While I am trying to upload a registry in my registries using the C# code , the application is throwing the error "a required privilege is not held by the client". If I am using the same code on some other machine it is working fine but not particularly on my machine
I am using below mentioned code to upload the registry files
Process my_p = new Process();
my_p.StartInfo.FileName = "reg";
my_p.StartInfo.Arguments = "load HKLM\TEST C:\Documents and Settings\Administrator\NTUSER.DAT";
my_p.Start();
my_p.WaitForExit();
System.IO.StreamReader srOutPut = my_p.StandardOutput;
System.IO.StreamReader srError = my_p.StandardError;
my_p.Close();
results = srOutPut.ReadToEnd().Trim();
Errors = srError.ReadToEnd().Trim();
Moreover One thing I have noticed that the above code I am using in http based web site, but when I am using it in a File Syatem based web site it is working fine. Please help I am not getting the error.
Regards,
Vikram
You can load RegLoadKey function directly to load the hive as a subkey. Another API is RegLoadAppKey but it works only starting from Windows Vista. Different versions of Reg.exe use the API. How you can read in the description the RegLoadKey you need have SE_RESTORE_NAME and SE_BACKUP_NAME privileges and enable these (see http://msdn.microsoft.com/en-us/library/ms717797.aspx). If you are in the Group of Administrators or Backup Operators you have these privileges. One more problem can be if Reg.exe use RegLoadKey function and you have a local administrative rights, but you start on Vista or Windows 7 a command without admin rights because of UAC (User Account Control).

Webdav failing to use integrated authentication since domain migration

We have a program that uses webdav, authenticating with windows authentication so users don't have to type their username and password. this worked fine until we migrated domain. now the code that used to connect returns error code 5 (access denied):
string psPassword =null, psUsername=null;
structNetResource stNetRes = new structNetResource();
stNetRes.iScope = 2;
stNetRes.iType = RESOURCETYPE_DISK;
stNetRes.iDisplayType = 3;
stNetRes.iUsage = 1;
stNetRes.sRemoteName = WebDAVServerpath;
stNetRes.sLocalName = null; //connect, but don't show drive in my computer
int iFlags = CONNECT_CMD_SAVECRED;
int i = WNetAddConnection2A(ref stNetRes, psPassword, psUsername, iFlags);
Does anyone have any idea how to fix this? I've tried running
int j = WNetCancelConnection2A(WebDAVServerpath, CONNECT_UPDATE_PROFILE, 0);
beforehand to clear any old connections but this isn't working.
(Running net use \\server.domain.com\share now prompts for a username and password whereas before it used to connect straight away, so it looks like something's interring with windows authentication.)
Edit - This is a WinForms application, IIS and the shared files are stored on the same machine. The problem only occurs on machines that were migrated; PCs that were (and still are) outisde our domain are unaffected.
If your file server and web server are not the same machine, you need to set up the file server to trust the web server for delegation. This allows the file server to trust the credentials that the web server has authenticated instead of re-prompting for credentials on each request.

Categories

Resources