C#: How to logon to a share when using DirectoryInfo - c#

If I want to instantiate a DirectoryInfo object with an UNC path
DirectoryInfo myDI = new DirectoryInfo (#"\\server\share");
how can I pass a username / password that is required to access that share?
Thanks

In .NET 4 this class works perfectly for me to logon in none windows server via UNC.
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Web;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Web.Security;
namespace toolsforimpersonations
{
public class Impersonator
{
#region "Consts"
public const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_PROVIDER_DEFAULT = 0;
#endregion
#region "External API"
[DllImport("advapi32.dll", SetLastError = true)]
public static extern int LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool RevertToSelf();
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int CloseHandle(IntPtr hObject);
#endregion
#region "Methods"
//Public Sub PerformImpersonatedTask(ByVal username As String, ByVal domain As String, ByVal password As String, ByVal logonType As Integer, ByVal logonProvider As Integer, ByVal methodToPerform As Action)
public void PerformImpersonatedTask(string username, string domain, string password, int logonType, int logonProvider, Action methodToPerform)
{
IntPtr token = IntPtr.Zero;
if (RevertToSelf()) {
if (LogonUser(username, domain, password, logonType, logonProvider, ref token) != 0) {
dynamic identity = new WindowsIdentity(token);
dynamic impersonationContext = identity.Impersonate();
if (impersonationContext != null) {
methodToPerform.Invoke();
impersonationContext.Undo();
}
// do logging
} else {
}
}
if (token != IntPtr.Zero) {
CloseHandle(token);
}
}
#endregion
}
public class UserImpersonation
{
const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_LOGON_NETWORK = 3;
const int LOGON32_LOGON_BATCH = 4;
const int LOGON32_LOGON_SERVICE = 5;
const int LOGON32_LOGON_UNLOCK = 7;
const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_PROVIDER_WINNT35 = 1;
const int LOGON32_PROVIDER_WINNT40 = 2;
const int LOGON32_PROVIDER_WINNT50 = 3;
WindowsImpersonationContext impersonationContext;
[DllImport("advapi32.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
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, ExactSpelling = true)]
public static extern int DuplicateToken(IntPtr ExistingTokenHandle, int ImpersonationLevel, ref IntPtr DuplicateTokenHandle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern long RevertToSelf();
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern long CloseHandle(IntPtr handle);
public bool impersonateUser(string userName, string domain, string password)
{
return impersonateValidUser(userName, domain, password);
}
public void undoimpersonateUser()
{
undoImpersonation();
}
private bool impersonateValidUser(string userName, string domain, string password)
{
bool functionReturnValue = false;
WindowsIdentity tempWindowsIdentity = null;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
functionReturnValue = false;
//if (RevertToSelf()) {
if (LogonUserA(userName, domain, password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50, ref token) != 0) {
if (DuplicateToken(token, 2, ref tokenDuplicate) != 0) {
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
if ((impersonationContext != null)) {
functionReturnValue = true;
}
}
}
//}
if (!tokenDuplicate.Equals(IntPtr.Zero)) {
CloseHandle(tokenDuplicate);
}
if (!token.Equals(IntPtr.Zero)) {
CloseHandle(token);
}
return functionReturnValue;
}
private void undoImpersonation()
{
impersonationContext.Undo();
}
}
}
And this is how to use from your programm
UserImpersonation impersonator = new UserImpersonation();
impersonator.impersonateUser("username", "", "password"); //No Domain is required
List<FileInfo> OcDialerlfinfo = null;
OcDialerlfinfo = GetFileList("*", "\\\\10.11.11.122\\shared_folder");
impersonator.undoimpersonateUser();
and for the sake of the test you need to use this little function to list all the files in the directory
public static List<FileInfo> GetFileList(string fileSearchPattern, string rootFolderPath)
{
DirectoryInfo rootDir = new DirectoryInfo(rootFolderPath);
List<DirectoryInfo> dirList = new List<DirectoryInfo>(rootDir.GetDirectories("*", SearchOption.AllDirectories));
dirList.Add(rootDir);
List<FileInfo> fileList = new List<FileInfo>();
foreach (DirectoryInfo dir in dirList)
{
fileList.AddRange(dir.GetFiles(fileSearchPattern, SearchOption.TopDirectoryOnly));
}
return fileList;
}

Try this approach:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;
namespace Tools.Network
{
public enum LogonType
{
LOGON32_LOGON_INTERACTIVE = 2,
LOGON32_LOGON_NETWORK = 3,
LOGON32_LOGON_BATCH = 4,
LOGON32_LOGON_SERVICE = 5,
LOGON32_LOGON_UNLOCK = 7,
LOGON32_LOGON_NETWORK_CLEARTEXT = 8, // Win2K or higher
LOGON32_LOGON_NEW_CREDENTIALS = 9 // Win2K or higher
};
public enum LogonProvider
{
LOGON32_PROVIDER_DEFAULT = 0,
LOGON32_PROVIDER_WINNT35 = 1,
LOGON32_PROVIDER_WINNT40 = 2,
LOGON32_PROVIDER_WINNT50 = 3
};
public enum ImpersonationLevel
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3
}
class Win32NativeMethods
{
[DllImport("advapi32.dll", SetLastError = true)]
public 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)]
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);
}
/// <summary>
/// Allows code to be executed under the security context of a specified user account.
/// </summary>
/// <remarks>
///
/// Implements IDispose, so can be used via a using-directive or method calls;
/// ...
///
/// var imp = new Impersonator( "myUsername", "myDomainname", "myPassword" );
/// imp.UndoImpersonation();
///
/// ...
///
/// var imp = new Impersonator();
/// imp.Impersonate("myUsername", "myDomainname", "myPassword");
/// imp.UndoImpersonation();
///
/// ...
///
/// using ( new Impersonator( "myUsername", "myDomainname", "myPassword" ) )
/// {
/// ...
/// [code that executes under the new context]
/// ...
/// }
///
/// ...
/// </remarks>
public class Impersonator : IDisposable
{
private WindowsImpersonationContext _wic;
/// <summary>
/// Begins impersonation with the given credentials, Logon type and Logon provider.
/// </summary>
///
<param name="userName">Name of the user.</param>
///
<param name="domainName">Name of the domain.</param>
///
<param name="password">The password. <see cref="System.String"/></param>
///
<param name="logonType">Type of the logon.</param>
///
<param name="logonProvider">The logon provider. <see cref="Mit.Sharepoint.WebParts.EventLogQuery.Network.LogonProvider"/></param>
public Impersonator(string userName, string domainName, string password, LogonType logonType, LogonProvider logonProvider)
{
Impersonate(userName, domainName, password, logonType, logonProvider);
}
/// <summary>
/// Begins impersonation with the given credentials.
/// </summary>
///
<param name="userName">Name of the user.</param>
///
<param name="domainName">Name of the domain.</param>
///
<param name="password">The password. <see cref="System.String"/></param>
public Impersonator(string userName, string domainName, string password)
{
Impersonate(userName, domainName, password, LogonType.LOGON32_LOGON_INTERACTIVE, LogonProvider.LOGON32_PROVIDER_DEFAULT);
}
/// <summary>
/// Initializes a new instance of the <see cref="Impersonator"/> class.
/// </summary>
public Impersonator()
{}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
UndoImpersonation();
}
/// <summary>
/// Impersonates the specified user account.
/// </summary>
///
<param name="userName">Name of the user.</param>
///
<param name="domainName">Name of the domain.</param>
///
<param name="password">The password. <see cref="System.String"/></param>
public void Impersonate(string userName, string domainName, string password)
{
Impersonate(userName, domainName, password, LogonType.LOGON32_LOGON_INTERACTIVE, LogonProvider.LOGON32_PROVIDER_DEFAULT);
}
/// <summary>
/// Impersonates the specified user account.
/// </summary>
///
<param name="userName">Name of the user.</param>
///
<param name="domainName">Name of the domain.</param>
///
<param name="password">The password. <see cref="System.String"/></param>
///
<param name="logonType">Type of the logon.</param>
///
<param name="logonProvider">The logon provider. <see cref="Mit.Sharepoint.WebParts.EventLogQuery.Network.LogonProvider"/></param>
public void Impersonate(string userName, string domainName, string password, LogonType logonType, LogonProvider logonProvider)
{
UndoImpersonation();
IntPtr logonToken = IntPtr.Zero;
IntPtr logonTokenDuplicate = IntPtr.Zero;
try
{
// revert to the application pool identity, saving the identity of the current requestor
_wic = WindowsIdentity.Impersonate(IntPtr.Zero);
// do logon & impersonate
if (Win32NativeMethods.LogonUser(userName,
domainName,
password,
(int)logonType,
(int)logonProvider,
ref logonToken) != 0)
{
if (Win32NativeMethods.DuplicateToken(logonToken, (int)ImpersonationLevel.SecurityImpersonation, ref logonTokenDuplicate) != 0)
{
var wi = new WindowsIdentity(logonTokenDuplicate);
wi.Impersonate(); // discard the returned identity context (which is the context of the application pool)
}
else
throw new Win32Exception(Marshal.GetLastWin32Error());
}
else
throw new Win32Exception(Marshal.GetLastWin32Error());
}
finally
{
if (logonToken != IntPtr.Zero)
Win32NativeMethods.CloseHandle(logonToken);
if (logonTokenDuplicate != IntPtr.Zero)
Win32NativeMethods.CloseHandle(logonTokenDuplicate);
}
}
/// <summary>
/// Stops impersonation.
/// </summary>
private void UndoImpersonation()
{
// restore saved requestor identity
if (_wic != null)
_wic.Undo();
_wic = null;
}
}
}

Related

how to use network credential to connect share folder somewhere in the network?

this is my code and I want to know how to use network credentials in my code
string filePath = Path.Combine(#"\\192.168.5.90\uploads", newfilename);
using (var filestream = new FileStream(filePath, FileMode.Create,FileAccess.Write))
{
await uploadfile.CopyToAsync(filestream);
}
return Ok(newfilename);
Windows uses the identity of the user running the process of the app for this authentication.
You will need to impersonate an alternative user and execute the code that writes that file in that impersonation context. Look into WindowsIdentity.RunImpersonated method
https://learn.microsoft.com/en-us/dotnet/api/system.security.principal.windowsidentity.runimpersonated?view=netcore-3.1
you can use this link
You can use a impersonator instead:
using (var impersonator = new Impersonator(username, password))
{
File.Copy(source, destination, true);
}
this is a copy past from our implementation, so please adjust your domain name
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
public class Impersonator : IDisposable
{
/// <summary>
/// The Impersonator class is used to access a network share with other credentials.
/// </summary>
private readonly WindowsImpersonationContext _impersonatedUser;
private readonly IntPtr _userHandle;
/// <summary>
/// Constructor
/// </summary>
/// <param name="username">The user of the network share</param>
/// <param name="password">The password of the network share</param>
public Impersonator(string username, string password, string userDomain = "YOURDOMAIN")
{
_userHandle = new IntPtr(0);
bool returnValue = LogonUser(username, userDomain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
ref _userHandle);
if (!returnValue)
throw new ApplicationException(
"The applications wasn't able to impersonate the user with the specified credentials!");
var newId = new WindowsIdentity(_userHandle);
_impersonatedUser = newId.Impersonate();
}
#region IDisposable Members
public void Dispose()
{
if (_impersonatedUser != null)
{
_impersonatedUser.Undo();
CloseHandle(_userHandle);
}
}
#endregion
#region Interop imports/constants
public const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_LOGON_SERVICE = 3;
public const int LOGON32_PROVIDER_DEFAULT = 0;
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern bool LogonUser(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType,
int dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
#endregion
}

Getting HttpOnly cookies. Returns only one when expecting multiple

I found a working solution to get HttpOnly cookies, however it only returns one cookie, while I expect multiple cookies.
Can somebody tell me what I do wrong?
private const Int32 InternetCookieHttponly = 0x2000;
[DllImport("wininet.dll", SetLastError = true)]
public static extern bool InternetGetCookieEx(string pchURL, string pchCookieName, StringBuilder pchCookieData, ref uint pcchCookieData, int dwFlags, IntPtr lpReserved);
const int INTERNET_COOKIE_HTTPONLY = 0x00002000;
public static string GetGlobalCookies(string uri)
{
uint datasize = 1024;
StringBuilder cookieData = new StringBuilder((int)datasize);
if (InternetGetCookieEx(uri, "cookiename", cookieData, ref datasize, INTERNET_COOKIE_HTTPONLY, IntPtr.Zero)
&& cookieData.Length > 0)
{
return cookieData.ToString().Replace(';', ',');
}
else
{
return null;
}
}
The pchCookieName parameter is the case-sensitive name of the cookie to retrieve. You are passing in the string "cookiename", so the function will only return that cookie.
According to this MSDN code sample, you can retrieve all cookies by passing null to this parameter.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace MSDN.Samples.ClaimsAuth
{
/// <summary>
/// WinInet.dll wrapper
/// </summary>
internal static class CookieReader
{
/// <summary>
/// Enables the retrieval of cookies that are marked as "HTTPOnly".
/// Do not use this flag if you expose a scriptable interface,
/// because this has security implications. It is imperative that
/// you use this flag only if you can guarantee that you will never
/// expose the cookie to third-party code by way of an
/// extensibility mechanism you provide.
/// Version: Requires Internet Explorer 8.0 or later.
/// </summary>
private const int INTERNET_COOKIE_HTTPONLY = 0x00002000;
[DllImport("wininet.dll", SetLastError = true)]
private static extern bool InternetGetCookieEx(
string url,
string cookieName,
StringBuilder cookieData,
ref int size,
int flags,
IntPtr pReserved);
/// <summary>
/// Returns cookie contents as a string
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static string GetCookie(string url)
{
int size = 512;
StringBuilder sb = new StringBuilder(size);
if (!InternetGetCookieEx(url, null, sb, ref size, INTERNET_COOKIE_HTTPONLY, IntPtr.Zero))
{
if (size < 0)
{
return null;
}
sb = new StringBuilder(size);
if (!InternetGetCookieEx(url, null, sb, ref size, INTERNET_COOKIE_HTTPONLY, IntPtr.Zero))
{
return null;
}
}
return sb.ToString();
}
}
}

Impersonation causing unhandled exception

I have a C# windows application that I need to impersonate a userid other than the one that the user logged on with when I access files on a file share. I use impersonation to switch to this user and then switch back. I also need to revert back to the logged on user to access local files. I get a System.ExecutionEngineException that is not being caught by the application and it just stops. Here is the code. Any ideas why I am getting this error or suggestions on how to do it better.
Edit: The application is using the XAF framework by DevExpress.
public static class Impersonation
{
#region Fields
public static WindowsImpersonationContext newUser;
public static string domain;
public static string userName;
public static string password;
#endregion
// group type enum
private enum SECURITY_IMPERSONATION_LEVEL : int
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3
}
// obtains user token
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool LogonUser(string pszUsername, string pszDomain, string pszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
// closes open handes returned by LogonUser
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private extern static bool CloseHandle(IntPtr handle);
// creates duplicate token handle
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
#region Methods
public static void RevertUser()
{
newUser.Undo();
}
#region ImpersonateApexApplicationUser
/// <summary>
/// Attempts to impersonate a user. If successful, returns
/// a WindowsImpersonationContext of the new users identity.
/// </summary>
/// <param name="sUsername">Username you want to impersonate</param>
/// <param name="sDomain">Logon domain</param>
/// <param name="sPassword">User's password to logon with</param></param>
/// <returns></returns>
public static void ImpersonateApexApplicationUser()
{
// initialize tokens
IntPtr pExistingTokenHandle = new IntPtr(0);
IntPtr pDuplicateTokenHandle = new IntPtr(0);
pExistingTokenHandle = IntPtr.Zero;
pDuplicateTokenHandle = IntPtr.Zero;
try
{
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
if (!LogonUser(userName, domain, Encryption.Decrypt(password), LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref pExistingTokenHandle))
throw new Exception(string.Format("LogonUser() failed with error code {0}", Marshal.GetLastWin32Error()));
else
if (!DuplicateToken(pExistingTokenHandle, (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, ref pDuplicateTokenHandle))
{
int errorCode = Marshal.GetLastWin32Error();
CloseHandle(pExistingTokenHandle); // close existing handle
throw new Exception(string.Format("DuplicateToken() failed with error code: {0}", errorCode));
}
else
{
// create new identity using new primary token
WindowsIdentity newId = new WindowsIdentity(pDuplicateTokenHandle);
newUser = newId.Impersonate();
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
// close handle(s)
if (pExistingTokenHandle != IntPtr.Zero)
CloseHandle(pExistingTokenHandle);
if (pDuplicateTokenHandle != IntPtr.Zero)
CloseHandle(pDuplicateTokenHandle);
}
}
#endregion
}
I call it like this once at the beginning of the program:
Impersonation.domain = "xxxx";
Impersonation.userName = "xxxx;
Impersonation.password = "xxxx";
Impersonation.ImpersonateApexApplicationUser();
and then:
Impersonation.ImpersonateApexApplicationUser();
//file share access code
Impersonation.RevertUser();

Impersonating a Windows user

I am using the code to impersonate a user account to get access to a file share.
public class Impersonator :
IDisposable
{
#region Public methods.
// ------------------------------------------------------------------
/// <summary>
/// Constructor. Starts the impersonation with the given credentials.
/// Please note that the account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
public Impersonator(
string userName,
string domainName,
string password )
{
ImpersonateValidUser( userName, domainName, password );
}
// ------------------------------------------------------------------
#endregion
#region IDisposable member.
// ------------------------------------------------------------------
public void Dispose()
{
UndoImpersonation();
}
// ------------------------------------------------------------------
#endregion
#region P/Invoke.
// ------------------------------------------------------------------
[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;
// ------------------------------------------------------------------
#endregion
#region Private member.
// ------------------------------------------------------------------
/// <summary>
/// Does the actual impersonation.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
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 );
}
}
}
/// <summary>
/// Reverts the impersonation.
/// </summary>
private void UndoImpersonation()
{
if ( impersonationContext!=null )
{
impersonationContext.Undo();
}
}
private WindowsImpersonationContext impersonationContext = null;
// ------------------------------------------------------------------
#endregion
}
Then using:
using (new Impersonator("username", "domain", "password"))
{
Process.Start("explorer.exe", #"/root,\\server01-Prod\abc");
}
I get an "Access Denied" error.
This user supposely has access to this share. I can map a drive, use "net use" but this code will not work. Now I am thinking it is the code. Does anyone see anything? Is there a better way of doing this?
try this :
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(
string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
out IntPtr phToken);
Usage :
IntPtr userToken = IntPtr.Zero;
bool success = External.LogonUser(
"john.doe",
"domain.com",
"MyPassword",
(int) AdvApi32Utility.LogonType.LOGON32_LOGON_INTERACTIVE, //2
(int) AdvApi32Utility.LogonProvider.LOGON32_PROVIDER_DEFAULT, //0
out userToken);
if (!success)
{
throw new SecurityException("Logon user failed");
}
using (WindowsIdentity.Impersonate(userToken))
{
Process.Start("explorer.exe", #"/root,\\server01-Prod\abc");
}
If I'm understanding correctly, your intention is to run the process in the impersonation context.
The doc from CreateProcess (which is used by Process.Start) says:
If the calling process is impersonating another user, the new process uses the token for the calling process, not the impersonation token. To run the new process in the security context of the user represented by the impersonation token, use the CreateProcessAsUser or CreateProcessWithLogonW function.
So, you're using the wrong API for doing that.
Instead of using your Impersonator class, what happens when you call Process.Start and pass in a ProcessStartInfo instance that contains the username, password and domain that you want to run the process as?
Perhaps, if that works, then your Impersonator class should create a ProcessStartInfo instance and use that to create new processes (encapsulate that within the class itself).
var psi = new ProcessStartInfo("explorer.exe", #"/root,\\server01-Prod\abc");
psi.Domain = domain;
psi.UserName = username;
psi.Password = password;
psi.WorkingDirectory = workingDir;
Process.Start(psi);
Also, per the MSDN docs...
Setting the Domain, UserName, and the Password properties in a
ProcessStartInfo object is the recommended practice for starting a
process with user credentials.
You should also set the working directory when starting a process with different user creds.

How to print PDF document from Windows Service

I have windows service that must periodically print PDF document from server. My function is
private void PrintFormPdfData(byte[] formPdfData)
{
string tempFile;
tempFile = Path.GetTempFileName();
using (FileStream fs = new FileStream(tempFile, FileMode.Create))
{
fs.Write(formPdfData, 0, formPdfData.Length);
fs.Flush();
}
string pdfArguments = string.Format("/p /h\"{0}\"", tempFile);
string pdfPrinterLocation = #"C:\Program Files (x86)\Adobe\Reader 9.0\Reader\AcroRd32.exe";
ProcessStartInfo newProcess = new ProcessStartInfo(pdfPrinterLocation, pdfArguments);
newProcess.CreateNoWindow = true;
newProcess.RedirectStandardOutput = true;
newProcess.UseShellExecute = false;
newProcess.RedirectStandardError = true;
Process pdfProcess = new Process();
pdfProcess.StartInfo = newProcess;
pdfProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
pdfProcess.Start();
pdfProcess.WaitForExit();
}
When I implement this in Windows Application it works, but when I implement in Windows service it does not work.
Can you help me?
I solved problem with Session 0. I use this class:
public class ProcessStarter : IDisposable
{
#region Import Section
private static uint STANDARD_RIGHTS_REQUIRED = 0x000F0000;
private static uint STANDARD_RIGHTS_READ = 0x00020000;
private static uint TOKEN_ASSIGN_PRIMARY = 0x0001;
private static uint TOKEN_DUPLICATE = 0x0002;
private static uint TOKEN_IMPERSONATE = 0x0004;
private static uint TOKEN_QUERY = 0x0008;
private static uint TOKEN_QUERY_SOURCE = 0x0010;
private static uint TOKEN_ADJUST_PRIVILEGES = 0x0020;
private static uint TOKEN_ADJUST_GROUPS = 0x0040;
private static uint TOKEN_ADJUST_DEFAULT = 0x0080;
private static uint TOKEN_ADJUST_SESSIONID = 0x0100;
private static uint TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);
private static uint TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID);
private const uint NORMAL_PRIORITY_CLASS = 0x0020;
private const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;
private const uint MAX_PATH = 260;
private const uint CREATE_NO_WINDOW = 0x08000000;
private const uint INFINITE = 0xFFFFFFFF;
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
public enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
}
public enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation
}
public enum WTS_CONNECTSTATE_CLASS
{
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
private struct WTS_SESSION_INFO
{
public Int32 SessionID;
[MarshalAs(UnmanagedType.LPStr)]
public String pWinStationName;
public WTS_CONNECTSTATE_CLASS State;
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern uint WTSGetActiveConsoleSessionId();
[DllImport("wtsapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool WTSQueryUserToken(int sessionId, out IntPtr tokenHandle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool DuplicateTokenEx(IntPtr existingToken, uint desiredAccess, IntPtr tokenAttributes, SECURITY_IMPERSONATION_LEVEL impersonationLevel, TOKEN_TYPE tokenType, out IntPtr newToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool CreateProcessAsUser(IntPtr token, string applicationName, string commandLine, ref SECURITY_ATTRIBUTES processAttributes, ref SECURITY_ATTRIBUTES threadAttributes, bool inheritHandles, uint creationFlags, IntPtr environment, string currentDirectory, ref STARTUPINFO startupInfo, out PROCESS_INFORMATION processInformation);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool CloseHandle(IntPtr handle);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetLastError();
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int WaitForSingleObject(IntPtr token, uint timeInterval);
[DllImport("wtsapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int WTSEnumerateSessions(System.IntPtr hServer, int Reserved, int Version, ref System.IntPtr ppSessionInfo, ref int pCount);
[DllImport("userenv.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit);
[DllImport("wtsapi32.dll", ExactSpelling = true, SetLastError = false)]
public static extern void WTSFreeMemory(IntPtr memory);
[DllImport("userenv.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);
#endregion
/// <summary>
/// Initializes a new instance of the <see cref="ProcessStarter"/> class.
/// </summary>
public ProcessStarter()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ProcessStarter"/> class.
/// </summary>
/// <param name="processName">Name of the process.</param>
/// <param name="fullExeName">Full name of the exe.</param>
public ProcessStarter(string processName, string fullExeName)
{
processName_ = processName;
processPath_ = fullExeName;
}
/// <summary>
/// Initializes a new instance of the <see cref="ProcessStarter"/> class.
/// </summary>
/// <param name="processName">Name of the process.</param>
/// <param name="fullExeName">Full name of the exe.</param>
/// <param name="arguments">The arguments.</param>
public ProcessStarter(string processName, string fullExeName, string arguments)
{
processName_ = processName;
processPath_ = fullExeName;
arguments_ = arguments;
}
/// <summary>
/// Gets the current user token.
/// </summary>
/// <returns></returns>
public static IntPtr GetCurrentUserToken()
{
IntPtr currentToken = IntPtr.Zero;
IntPtr primaryToken = IntPtr.Zero;
IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
int dwSessionId = 0;
IntPtr hUserToken = IntPtr.Zero;
IntPtr hTokenDup = IntPtr.Zero;
IntPtr pSessionInfo = IntPtr.Zero;
int dwCount = 0;
WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref dwCount);
Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
Int32 current = (int)pSessionInfo;
for (int i = 0; i < dwCount; i++)
{
WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTS_SESSION_INFO));
if (WTS_CONNECTSTATE_CLASS.WTSActive == si.State)
{
dwSessionId = si.SessionID;
break;
}
current += dataSize;
}
WTSFreeMemory(pSessionInfo);
bool bRet = WTSQueryUserToken(dwSessionId, out currentToken);
if (bRet == false)
{
return IntPtr.Zero;
}
bRet = DuplicateTokenEx(currentToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out primaryToken);
if (bRet == false)
{
return IntPtr.Zero;
}
return primaryToken;
}
/// <summary>
/// Runs this instance.
/// </summary>
public void Run()
{
IntPtr primaryToken = GetCurrentUserToken();
if (primaryToken == IntPtr.Zero)
{
return;
}
STARTUPINFO StartupInfo = new STARTUPINFO();
processInfo_ = new PROCESS_INFORMATION();
StartupInfo.cb = Marshal.SizeOf(StartupInfo);
SECURITY_ATTRIBUTES Security1 = new SECURITY_ATTRIBUTES();
SECURITY_ATTRIBUTES Security2 = new SECURITY_ATTRIBUTES();
string command = "\"" + processPath_ + "\"";
if ((arguments_ != null) && (arguments_.Length != 0))
{
command += " " + arguments_;
}
IntPtr lpEnvironment = IntPtr.Zero;
bool resultEnv = CreateEnvironmentBlock(out lpEnvironment, primaryToken, false);
if (resultEnv != true)
{
int nError = GetLastError();
}
CreateProcessAsUser(primaryToken, null, command, ref Security1, ref Security2, false, CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT, lpEnvironment, null, ref StartupInfo, out processInfo_);
DestroyEnvironmentBlock(lpEnvironment);
CloseHandle(primaryToken);
}
/// <summary>
/// Stops this instance.
/// </summary>
public void Stop()
{
Process[] processes = Process.GetProcesses();
foreach (Process current in processes)
{
if (current.ProcessName == processName_)
{
current.Kill();
}
}
}
/// <summary>
/// Waits for exit.
/// </summary>
/// <returns></returns>
public int WaitForExit()
{
WaitForSingleObject(processInfo_.hProcess, INFINITE);
int errorcode = GetLastError();
return errorcode;
}
#region IDisposable Members
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
}
#endregion
private string processPath_ = string.Empty;
private string processName_ = string.Empty;
private string arguments_ = string.Empty;
private PROCESS_INFORMATION processInfo_;
/// <summary>
/// Gets or sets the process path.
/// </summary>
/// <value>The process path.</value>
public string ProcessPath
{
get
{
return processPath_;
}
set
{
processPath_ = value;
}
}
/// <summary>
/// Gets or sets the name of the process.
/// </summary>
/// <value>The name of the process.</value>
public string ProcessName
{
get
{
return processName_;
}
set
{
processName_ = value;
}
}
/// <summary>
/// Gets or sets the arguments.
/// </summary>
/// <value>The arguments.</value>
public string Arguments
{
get
{
return arguments_;
}
set
{
arguments_ = value;
}
}
}
Now, my function look like:
private void PrintFormPdfData(byte[] formPdfData)
{
string tempFile;
tempFile = Path.GetTempFileName();
using (FileStream fs = new FileStream(tempFile, FileMode.Create))
{
fs.Write(formPdfData, 0, formPdfData.Length);
fs.Flush();
}
string pdfArguments =string.Format("/t /o {0} \"Printer name\"", tempFile);
string pdfPrinterLocation = #"C:\Program Files (x86)\Adobe\Reader 9.0\Reader\AcroRd32.exe";
try
{
ProcessStarter processStarter = new ProcessStarter("AcroRd32", pdfPrinterLocation, pdfArguments);
processStarter.Run();
processStarter.WaitForExit();
processStarter.Stop();
}
finally
{
File.Delete(tempFile);
}
}
Also, ServiceProcessInstaller must have `Account set to "LocalSystem". When I created service I set to "Local service" with this user it does not work. I did not try with "Network service" or "User".
I solved this problem with the registry edits found in this article: https://support.microsoft.com/en-us/kb/184291.
Printing from Windows services (and also IIS) uses the SYSTEM account as I understand it. This account does not have access to the printer but these registry edits gives that SYSTEM account printing privileges.
Here is a summary:
export 3 keys ("folders within regedit.exe") to .reg files:
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\Current Version\Devices
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\Current Version\PrinterPorts
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\Current Version\Windows
edit your 3 newly created reg files and change "HKEY_CURRENT_USER" to "HKEY_USERS\.DEFAULT"
run your 3 reg files by double clicking them in explorer (this adds them to the registry)
That's all it takes. This helped me to print pdf files to a printer from PHP in IIS 7. My php script would work just fine so long as the server happened to have the Administrator logged in when the script was run. Now no one needs to be logged in and the script still works :)
I hope this saves someone as much searching and tinkering as I put in to find it.
You didn't mention the OS, but I suspect you are running into the Session 0 isolation feature that was added in Windows Vista, Server 2008 and subsequent OS releases
Application classes affected by this feature include:
Services that create UI.
A service that tries to use window-message functions such as SendMessage and PostMessage to communicate with an application.
Applications creating globally named objects.

Categories