I want to read files from a folder that exists on the network.
When I try to access this folder manually (from run command giving a path like \\ABCServer\Documents ) it ask me for credentials (username and password). After giving the correct credentials I am able to access/read files.
When I try to read the same files from C# code in ASP.NET it gives me an error:
Login Failure: unkown username or bad password
How can I pass credentials via C# code during reading file?
Below is a part of the code that I am using:
Stream s = File.OpenRead(filePath);
byte[] buffer = new byte[s.Length];
try
{
s.Read(buffer, 0, (Int32)s.Length);
}
finally
{
s.Close();
}
Note:
The code works fine
I'm using ASP.NET 4.0 with C#
IIS version is 7.5
This is from http://support.microsoft.com/kb/306158
public const int LOGON32_LOGON_INTERACTIVE=2;
public const int LOGON32_PROVIDER_DEFAULT=0;
WindowsImpersonationContext impersonationContext;
[DllImport("advapi32.dll")]
public static extern int LogonUserA(String lpszUserName,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern int DuplicateToken(IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
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();
}
You can do this using impersonation. Here is a question and answers of how it is done.
Related
I am trying to programmatically write a file on a mapped network drive. The share is hosted on a Synology NAS running DSM.
When I map the drive on Windows 10, I specify the share "\\192.1.1.1\Share", and when prompted for credentials I supply "DS3617XS\admin" and "password".
I have attempted to Impersonate the user before writing the file.
static void Main( string[] args )
{
using ( new Impersonator("admin", "DS3617XS", "password" ) )
{
//Write the file
}
}
public class Impersonator
{
[DllImport("advapi32.dll", SetLastError=true)]
private static extern int LogonUser(
string lpszUserName,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
private static extern int DuplicateToken(
IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
private static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private static extern bool CloseHandle(
IntPtr handle);
private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;
public Impersonator(
string userName,
string domainName,
string password )
{
ImpersonateValidUser( userName, domainName, password );
}
private void ImpersonateValidUser(
string userName,
string domain,
string password )
{
WindowsIdentity tempWindowsIdentity = null;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
try
{
if ( RevertToSelf() )
{
if ( LogonUser(
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();
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
finally
{
if ( token!= IntPtr.Zero )
{
CloseHandle( token );
}
if ( tokenDuplicate!=IntPtr.Zero )
{
CloseHandle( tokenDuplicate );
}
}
}
private WindowsImpersonationContext impersonationContext = null;
}
An Exception is thrown "There are currently no logon servers available to service the logon request"
I've read tons of articles about this error.. also trying to set the folder's permission to the lowest:
But I still getting the error: Access to the path '\servername\shareFolder$\folderNameWeWantToCreate' is denied.
I am trying to check whether a directory exists and try to create a directory if not using the below code:
string Folderpath = #"\\servername\shareFolder$\folderNameWeWantToCreate";
if (!Directory.Exists(FolderPath))
Directory.CreateDirectory(FolderPath);
The above code works well on local, but the error comes out when put on to the server.
Is there anything I did wrong??
You have to impersonate identity for user
http://technet.microsoft.com/en-us/library/cc730708%28v=ws.10%29.aspx
There are two possible solutions:
Application Pool Identity
You need to create a new Application Pool in IIS that runs as the LocalSystem user and change your application to run on that AppPool. You need a high privilege user to do stuff on the local HD anyway and this'll save you a lot of permission slinging. In the future if you want to harden security you can always go back to a low privilege AppPool and just grant it permissions wherever it needs them.
Remote User Impersonation
If the above doesn't work, this is your recourse. It seems tricky but the code I'm running works perfectly on ASP.NET MVC 5 / .NET 4.5.1 so it should run fine on older versions too. Here's the code:
Helper classes:
public class ImpersonationHelper
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
public const int LOGON32_PROVIDER_DEFAULT = 0;
public const int LOGON32_PROVIDER_WINNT50 = 3;
public const int LOGON32_PROVIDER_WINNT40 = 2;
public const int LOGON32_PROVIDER_WINNT35 = 1;
public const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_LOGON_NETWORK = 3;
public const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
public static SafeTokenHandle GetSafeTokenHandle(string userName, string password, string domain)
{
SafeTokenHandle safeTokenHandle;
bool returnValue = LogonUser(userName, domain, password,
LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50,
out safeTokenHandle);
if (false == returnValue)
{
int ret = Marshal.GetLastWin32Error();
throw new System.ComponentModel.Win32Exception(ret);
}
return safeTokenHandle;
}
}
public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeTokenHandle()
: base(true)
{
}
[DllImport("kernel32.dll")]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr handle);
protected override bool ReleaseHandle()
{
return CloseHandle(handle);
}
}
Main code:
var remoteUser = ConfigurationManager.AppSettings["RemoteUser"];
var remotePassword = ConfigurationManager.AppSettings["RemotePassword"];
var remoteDomain = ConfigurationManager.AppSettings["RemoteDomain"];
var safeTokenHandle = ImpersonationHelper.GetSafeTokenHandle(remoteUser, remotePassword, remoteDomain);
using (safeTokenHandle)
{
using (WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle()))
{
using (WindowsImpersonationContext impersonatedUser = newId.Impersonate())
{
// do stuff here the same as you would locally
}
}
}
My objective is to copy a folder from my system to a remote computer in c#.
I searched everywhere and found some information on how to do that. I am calling the LogonUser function with the domain, username and password, but it it fails and returns 0.
below is my piece of code. Can you please help me figure out the problem?
class Program
{
#region Assembly Functions
[DllImport("advapi32.dll")]
public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword,
int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);
[DllImport("kernel32.dll")]
public static extern bool CloseHandle(IntPtr handle);
#endregion
static void Main(string[] args)
{
SafeTokenHandle safeTokenHandle;
WindowsImpersonationContext impersonatedUser = null;
WindowsIdentity newId;
IntPtr tokenHandle;
IntPtr userHandle = IntPtr.Zero;
tokenHandle = IntPtr.Zero;
Console.WriteLine("Enter your domain.");
string domain = Console.ReadLine();
Console.WriteLine("Enter you user name.");
string uname = Console.ReadLine();
Console.WriteLine("Enter your password (Caution, password won't be hidden).");
string password = Console.ReadLine();
const int LOGON32_PROVIDER_DEFAULT = 0;
//This parameter causes LogonUser to create a primary token.
const int LOGON32_LOGON_INTERACTIVE = 2;
bool logon = LogonUser(uname, domain, password,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out safeTokenHandle);
if (false == logon)
{
int ret = Marshal.GetLastWin32Error();
Console.WriteLine("LogonUser failed with error code : {0}", ret);
throw new System.ComponentModel.Win32Exception(ret);
}
if (logon)
{
newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle());
using (impersonatedUser = newId.Impersonate())
{
// Check the identity.
Console.WriteLine("After impersonation: "
+ WindowsIdentity.GetCurrent().Name);
}
File.Copy(#"c:\result.xml", #"C:\result.xml", true);
}
//Undo impersonation
if (impersonatedUser != null)
{
impersonatedUser.Undo();
}
if (tokenHandle != IntPtr.Zero)
{
CloseHandle(tokenHandle);
}
}
}
public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeTokenHandle()
: base(true)
{
}
[DllImport("kernel32.dll")]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr handle);
protected override bool ReleaseHandle()
{
return CloseHandle(handle);
}
}
Why not use "Windows API Code Pack 1.1" which shows (among other things) how to use the shell to do e.g. drag and drop - which is what you essentially try to do.
If using the Shell, you don't have to think about how to logon and 1000 other things needed to support various situations.
Download the "Windows API Code Pack 1.1" package and look for DragAndDropWindow sample or some of the other samples.
I'm getting the following exception when going through some legacy impersonation logic:
Unable to find an entry point named 'LogonUser' in DLL 'advapi32.dll'
I understand that the error means that my app can't find the LogonUser method in the advapi32.dll.
The code looks something like this:
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
private static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
if(LogonUser(_username, _domainname, _password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref _tokenHandle))
{
//do stuff...
}
Has anyone had a similar error - any suggestions on how to fix it or why it is happening? Is there a better way to do this besides using the advapi32.dll (its a .net 3.5 solution but there are lots of legacy classes)?
Maybe it has something to do with the "ExactSpelling = true"
This seems to work:
public enum LogonType : int
{
Interactive = 2,
Network = 3,
Batch = 4,
Service = 5,
Unlock = 7,
NetworkCleartText = 8,
NewCredentials = 9,
}
public enum LogonProvider : int
{
Default = 0,
}
public class Impersonation : IDisposable
{
#region Dll Imports
[DllImport("kernel32.dll")]
private static extern Boolean CloseHandle(IntPtr hObject);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool LogonUser(string username, string domain,
string password, LogonType logonType,
LogonProvider logonProvider,
out IntPtr userToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool DuplicateToken(IntPtr token, int impersonationLevel,
ref IntPtr duplication);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool ImpersonateLoggedOnUser(IntPtr userToken);
#endregion
#region Private members
private bool _disposed;
private WindowsImpersonationContext _impersonationContext;
#endregion
#region Constructors
public Impersonation(String username, String domain, String password)
{
IntPtr userToken = IntPtr.Zero;
IntPtr userTokenDuplication = IntPtr.Zero;
// Logon with user and get token.
bool loggedOn = LogonUser(username, domain, password,
LogonType.Interactive, LogonProvider.Default,
out userToken);
if (loggedOn)
{
try
{
// Create a duplication of the usertoken, this is a solution
// for the known bug that is published under KB article Q319615.
if (DuplicateToken(userToken, 2, ref userTokenDuplication))
{
// Create windows identity from the token and impersonate the user.
WindowsIdentity identity = new WindowsIdentity(userTokenDuplication);
_impersonationContext = identity.Impersonate();
}
else
{
// Token duplication failed!
// Use the default ctor overload
// that will use Mashal.GetLastWin32Error();
// to create the exceptions details.
throw new Exception("Could not copy token");
}
}
finally
{
// Close usertoken handle duplication when created.
if (!userTokenDuplication.Equals(IntPtr.Zero))
{
// Closes the handle of the user.
CloseHandle(userTokenDuplication);
userTokenDuplication = IntPtr.Zero;
}
// Close usertoken handle when created.
if (!userToken.Equals(IntPtr.Zero))
{
// Closes the handle of the user.
CloseHandle(userToken);
userToken = IntPtr.Zero;
}
}
}
else
{
throw new Exception("Login failed");
}
}
~Impersonation()
{
Dispose(false);
}
#endregion
#region Public methods
public void Revert()
{
if (_impersonationContext != null)
{
// Revert to previous user.
_impersonationContext.Undo();
_impersonationContext = null;
}
}
#endregion
#region IDisposable implementation.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
Revert();
_disposed = true;
}
}
#endregion
}
Have you tried using the version of LogonUser's signature provided on pinvoke.net?
My ASP.NET 4.0 Web App is unable to access Network Printers, while debugging on VS 2010. It can access local printers. Seems like it may be a permissions issue. Since VS2010 Debugging runs on ASP.NET Development Server, it must be running under the account I used to log into Windows, right? Does that user need to be added as an Admin in that printers users? Is there any account that I can impersonate to get this working?
You are correct, debugging through Visual Studio means all of your code runs with the same rights as the user logged in to windows. On the server, you will need to setup impersonation and/or setup your Application Pool to run as a user who has access to print on these printers.
I recommend you setup a dedicated domain account (like domain\yourapp-impers-user) and either set the Application Pool to use that, or setup impersonation in your web.config. Then on the print server, you simply grant that user account the necessary permissions.
Here is an example of impersonation
using System;
using System.Collections.Generic;
using System.Web.Security;
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.Linq;
using System.Web;
using System.IO;
public partial class Main : PageBase
{
public const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_PROVIDER_DEFAULT = 0;
WindowsImpersonationContext impersonationContext;
[DllImport("advapi32.dll")]
public static extern int LogonUserA(String lpszUserName,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int DuplicateToken(IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack == false)
{
if (impersonateValidUser("Username", "Domain", "Password"))
{
//Your code under a specific user 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();
}
Good luck !
without knowing for sure, you can try to impersonate the Network Service, or of course your own domain account which should always work.