Found a solution, I think.
My problem was the code used for impersonating the user with write permissions on the destination folder.
(In my defence, all this project have been inherited from previous software company, and it's pretty massive, so keeping an eye on everything isn't that easy)
The impersonation process was wrapped in a class implementing IDisposable
public class Impersonator :
IDisposable
{
public Impersonator()
{
string userName = // get username from config
string password = // get password from config
string domainName = // get domain from config
ImpersonateValidUser(userName, domainName, password);
}
public void Dispose()
{
UndoImpersonation();
}
[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;
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 void UndoImpersonation()
{
if (impersonationContext != null)
{
impersonationContext.Undo();
}
}
private WindowsImpersonationContext impersonationContext = null;
}
This class is used this way:
using(new Impersonator())
{
// do stuff with files in here
}
My suspect was that closing the handlers of the impersonated user, somehow, it could break something in the way that windows handles file open by the impersonated user through a network share, as in my case, leaving shared files open in read+write mode, preventing any other process/user to open them.
I modified the Impersonator class as follows:
public class Impersonator :
IDisposable
{
public Impersonator()
{
string userName = // get username from config
string password = // get password from config
string domainName = // get domain from config
ImpersonateValidUser(userName, domainName, password);
}
public void Dispose()
{
UndoImpersonation();
impersonationContext.Dispose();
}
[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;
private void ImpersonateValidUser(
string userName,
string domain,
string password)
{
WindowsIdentity tempWindowsIdentity = null;
token = IntPtr.Zero;
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
{
}
}
private void UndoImpersonation()
{
try
{
if (impersonationContext != null)
{
impersonationContext.Undo();
}
}
finally
{
if (token != IntPtr.Zero)
{
CloseHandle(token);
}
if (tokenDuplicate != IntPtr.Zero)
{
CloseHandle(tokenDuplicate);
}
}
}
private WindowsImpersonationContext impersonationContext = null;
private IntPtr token;
private IntPtr tokenDuplicate;
}
Basically I moved the handlers closing in the UndoImpersonation method. Also I had a doubt about leaving the impersonationContext not explicitly disposed, si I disposed it in the Dispose method of the Impersonator class.
Since I put in production this update, I hadn't any other issue with this code, and not any other shared file left open in read+write mode on the destination server.
Maybe not the optimal solution (I still have a whole bunch of sessions in Computer Management > Shared Folders > Sessions, but this seems not harming the system, for now.
If anyone have some additional comment, suggestion or depth study about this situation, I will be please to read.
Thanks,
Claudio
destinationPathisn't what you think it is in certain cases. Perhaps you should log that too. - Charles Mager