What could be causing successive processes created via CreateProcessAsUser from web service using delegation to encounter System.UnauthorizedAccessException exceptions when trying to access network shares?
WCF Rest web service kicks off a new process using CreateProcessAsUser with the duplicated user token. The process kicks off and reports that it is owned by the impersonated user who has access rights to the share.
On a clean boot of the web server the process can call System.IO.Directory.Exists over and over on a network share directory (\\nasshare\test) and get the correct response. However when the process ends and another call the to web service is made to kick off the process again it will report that the directory no longer exists and my program code then tries to create the supposedly missing dir and fails with the exception message: System.UnauthorizedAccessException: Access to the path '\\nasshare\test' is denied.
Oddly, if I reboot the webserver it will work again...until the process ends. Successive processes created by the web service will fail. When I run the process from the command prompt on the web server the process can see the folder and write to it. I can run the process over and over in this way and it will work fine even immediately after it fails when launched using CreateProcessAsUser within the web server.
- Web Server: Windows Server 2008 R2
- Network share: Windows Storage Server 2003
- Web Service Hosting: IIS7
- Web Service Client: IE,Chrome
- Application Pool Identity: NetworkService
- .Net Framework: v4.0
Simplified Rest web service
[Description("Launch a process.")]
[WebGet(UriTemplate = "fragment/launch")]
public void LaunchProcess()
{
...
using (ServiceSecurityContext.Current.WindowsIdentity.Impersonate())
{
launchProcessAs(WindowsIdentity.GetCurrent().Token, @"c:\temp\test.exe", "1234"); //In this test the arg "1234" is not used
}
}//end rest func call
const UInt32 MAXIMUM_ALLOWED = 0x2000000;
const UInt32 GENERIC_ALL_ACCESS = 0x10000000;
const Int32 CREATE_NEW_PROCESS_GROUP = 0x00000200;
const Int32 CREATE_UNICODE_ENVIRONMENT = 0x00000400;
const Int32 IDLE_PRIORITY_CLASS = 0x40;
const Int32 NORMAL_PRIORITY_CLASS = 0x20;
const Int32 HIGH_PRIORITY_CLASS = 0x80;
const Int32 REALTIME_PRIORITY_CLASS = 0x100;
const Int32 CREATE_NEW_CONSOLE = 0x00000010;
const Int32 DETACHED_PROCESS = 0x00000008;
public bool launchProcessAs(IntPtr userToken, string pathToProcess, string processArgs)
{
bool success = false;
try
{
pathToProcess = System.IO.Path.GetFullPath(pathToProcess);
IntPtr DupedToken = new IntPtr(0);
bool duplicatedToken = false;
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.bInheritHandle = false;
sa.Length = Marshal.SizeOf(sa);
sa.lpSecurityDescriptor = (IntPtr)0;
const int SecurityImpersonation = (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation;
const int TokenType = 1;
duplicatedToken = DuplicateTokenEx(userToken, GENERIC_ALL_ACCESS, ref sa, SecurityImpersonation, TokenType, ref DupedToken);
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = "";
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
if (processArgs != null)
{
processArgs = " " + processArgs;
}
if (duplicatedToken)
{
success = CreateProcessAsUser(DupedToken, pathToProcess, processArgs, ref sa, ref sa, false, 0, IntPtr.Zero, null, ref si, out pi);
}
if (success)
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(DupedToken);
}
}
catch (Exception e)
{
success = false;
}
return success;
}
Simplified example of test.exe Program code
static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
{
String tempDir = "\\\\nasshare\\test";
String tempFile = tempDir + "\\newfile.txt";
// Create temp dir
if (!System.IO.Directory.Exists(tempDir)) //This reports erroneously after first run from a reboot from createProcessAsUser within web service but works everytime when run directly from cmd on web server by same user
{
System.IO.Directory.CreateDirectory(tempDir); // This fails with System.UnauthorizedAccessException after first run from a reboot when launched from createProcessAsUser but works everytime when run directly on web server by same user directly from cmd
}
File.WriteAllText(tempFile, "hello world");
}
}