I want to access some places on a remote machine.The folder i want to access have full control to EVERYONE. The code given below is used to access network path.
System.IO.DirectoryInfo locationInfo = new System.IO.DirectoryInfo(BackupLocationTxt.Text);
if (locationInfo.Exists)
{
// do some operations
}
The application run fine if both the host computer and the remote computer to be accessed having os windows xp .The application also run fine if the application is running inside visual studio .
Then my problems is ,any one of the machine( server and remote machine)having an os newer then windows xp( like windows 7, server 2008) locationInfo.Exists always false.
But if the application is running inside in visual studio, then it work fine independent of os
I searched a lot in net. But didnt find an exact solution yet. Someone suggests impersonation. But i dont know exactly how to do it. Is impersonations is the solution for my problem ?? Or is there any better ideas??
Any help will be greatly appreciated
You have an interesting problem, Null. How have you configured your sites Directory Security? If's Anonymous access is enabled, the folder open to Everyone may not be allowing access depending on the OS of the server (see this Microsoft KB Article for more information).
If the site is running as Anonymous, you could change the account that the site runs as in the IIS Manager, or you could enable Impersonation. When you are running the site in Visual Studio the site is running with your permissions, so Anonymous isn't a problem then.
You can use the following code to output the identity of the user that your site is running as to help figure out what is going on. You may be able to give the user your site run as access to the network location without any impersonation. Add a ASP:Label to you page and see who you are running as:
lblSomeLabel.Text = System.Security.Principal.WindowsIdentity.GetCurrent().Name
Impersonation could open you up to additional security risks, so you should do some more reading before making that change - but, the user you use for impersonation doesn't need to be a domain admin. In your case, the user may just need to have full access privileges to the network location.
You can read more about how to enable impersonation on this Microsoft KB Article. Below is some of the code from that page that I'd recommend. Rather than have your whole site run in impersonation mode, the code below runs only the part that you are having a problem with.
public void Page_Load(Object s, EventArgs e)
{
if(impersonateValidUser("username", "domain", "password"))
{
//Insert your code that runs under the security context of a specific user here.
undoImpersonation();
}
else
{
//Your impersonation failed. Therefore, include a fail-safe mechanism here.
}
}
private bool impersonateValidUser(String userName, String domain, String password)
{
WindowsIdentity tempWindowsIdentity;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if(RevertToSelf())
{
if(LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if(DuplicateToken(token, 2, ref tokenDuplicate) != 0)
{
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
if (impersonationContext != null)
{
CloseHandle(token);
CloseHandle(tokenDuplicate);
return true;
}
}
}
}
if(token!= IntPtr.Zero)
CloseHandle(token);
if(tokenDuplicate!=IntPtr.Zero)
CloseHandle(tokenDuplicate);
return false;
}
private void undoImpersonation()
{
impersonationContext.Undo();
}
Also, while serching for security articles I found this StackOverflow question that is worth a read.
Try System.IO.Directory.Exists instead.
Bear in mind that if you do not have at a minimum read-only permission to the directory, the Exists method will return false
Related
I am trying to get information about the currently logged in user via a Windows service, specifically is the user is an admin level role or not. The service is properly collecting the currently logged in user, and I have code that works when the computer the service is running on is connected to the network, but I can't always guarantee the computer will be on the network when the service does this user level check.
Long story short, does WindowsIdentity require the computer be connected to the network to create an identity? I can't find anything online that clearly states if this is the case.
I only need to get whether or not the current user is an admin level role or not for the local system. I don't care if it's an admin at the domain level, although I understand this is probably going to be the case if the system is on a network.
If there is a better way to do this for my needs, I am happy to see other recommendations.
I have done some major googling and looked at a number of other SO answers, but none of them seem to work when the computer is not on the network. They did help me get a version that works when the computer is on the network though.
I also looked at the MS documentation for this and don't see anything clear about network needs. I may have missed something thought.
The below method works while on network.
private bool IsUserAdministrator(string accountName)
{
var result = false;
try
{
using (var identity = new WindowsIdentity(accountName))
{
var principal = new WindowsPrincipal(identity);
var adminSid = new SecurityIdentifier("BA");
result = principal.UserClaims.Any(c => c.Value.Equals(adminSid.ToString()));
}
}
catch (Exception)
{
// Exception handled by returning false
}
return result;
}
This method also works, when on network. The failure point while debugging is the new WindowsIdentity().
private bool IsUserAdministrator(string accountName)
{
using (WindowsIdentity identity = new WindowsIdentity(accountName))
{
WindowsPrincipal principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
}
Once again, when connected to the network, both of these either return 'true' when the user is an admin role, or 'false' when the user is not admin role.
When not connected to the network this fails and throws the exception {"There are currently no logon servers available to service the logon request.\r\n"}.
Is this an issue with WindowsIdentity or is something else causing this? Is this an LDAP issue?
Sorry, I realize there are multiple questions spread throughout this, but the number one issue is, does WindowsIdentity need network connection, and if yes, is there another way to achieve my goals?
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
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.
I deployed my internal web application to server A and got an error when creating a file to a network drive on server B. If I run locally, the file got created on server B successfully.
System.UnauthorizedAccessException: Access to the path '\\b\folder\test.pdf' is denied.
The identity of the application pool is networkservice. And I gave networkservice full control on the destination folder on server B. I even gave Everyone full control, but it still got the error.
Server A runs .NET 7.5. Code to create file:
var byteArray = generateArray();
var destination = "\\\\b\\folder\\test.pdf";
try {
var destinationFile = new FileInfo(destination);
if (destinationFile.Exists) {
destinationFile.Delete();
}
System.IO.File.WriteAllBytes(destination, byteArray);
} catch (UnauthorizedAccessException) {
//
}
I've seen someone got the exact same problem here. But it didn't solve mine.
Solution:
I changed the identity to administrator account instead of using network service for the application pool. It works but I don't fully understand why it works. Because the network service on A is different than the one on B?
Even though you provided access to everyone, certain applications have to receive specific permission. This was apart of the UAC System introduced in Window's Vista. This move was to increase security, so an application couldn't run under any user and basically have full access.
What you should do, is on the directory provide the following access:
IIS AppPool\NameOfAppPool
That will provide specific access to your hosted web application to that directory, for IIS will be able to correctly manipulate the directory. Some code you could implement to help validate before you write or read, would be:
public static bool ValidateIOPermission(string path)
{
try
{
if(Directory.Exist(path))
return true;
else { Directory.CreateDirectory(path); }
}
catch(Exception ex) { return false; }
}
The above code is a small sample, basically try to perform the action and catch the exception, that way you know if you have access or not.
I'm researching on how to add a shortcut in the windows context menu to my application. I came across this article and I tried it out. This is the code it uses to create a key in the registry.
private void btnAddMenu_Click(object sender, System.EventArgs e)
{
RegistryKey regmenu = null;
RegistryKey regcmd = null;
try
{
regmenu = Registry.ClassesRoot.CreateSubKey(MenuName);
if(regmenu != null)
regmenu.SetValue("",this.txtName.Text);
regcmd = Registry.ClassesRoot.CreateSubKey(Command);
if(regcmd != null)
regcmd.SetValue("",this.txtPath.Text);
}
catch(Exception ex)
{
MessageBox.Show(this,ex.ToString());
}
finally
{
if(regmenu != null)
regmenu.Close();
if(regcmd != null)
regcmd.Close();
}
}
The problem is if I run it through my Administrator account, it works fine. But when I do it through a different account which doesn't have admin privileges, it throws this exception.
system.unauthorizedaccessexception access to the registry key is denied
Now if I were to use this code in one of my own applications to create a shortcut in the context menu, I can't be sure every user would run it as the Administrator, right?
Is there any way in C# to escalate the user privileges when creating the registry key?
If you know any other way to add an item to the windows context menu, I'd be interested in them too.
Thank you.
You cannot escalate permissions as such (at least I'd like to know about it, but doesn't seem possible as yet), but you need to run / start your app (embed into manifest) elevated.
Please take a look at these entries...
How do I force my .NET application to run as administrator?
Elevating process privilege programatically?
I'd suggest what comments said, running that from the setup. Or let your app run as admin from the start, or possibly jump start an elevated process from your app - when needed (e.g. running another exe of yours that has its manifest properly).
You could escalate your permissions much the same way installers do it. It will require user interaction, as that's the way the OS is designed (and rightly so) - you can't go around it.