ネットワーク共有にアクセスしようとしたときに、委任を使用してWebサービスからCreateProcessAsUserを介して作成された連続するプロセスで、System.UnauthorizedAccessException例外が発生する原因は何でしょうか。
WCF Rest Webサービスは、複製されたユーザートークンでCreateProcessAsUserを使用して新しいプロセスを開始します。プロセスが開始され、共有へのアクセス権を持つ偽装されたユーザーが所有していることが報告されます。
Webサーバーのクリーンブート時に、プロセスはSystem.IO.Directory.Existsをネットワーク共有ディレクトリ(\\ nasshare \ test)で何度も呼び出し、正しい応答を取得できます。ただし、プロセスが終了し、プロセスを再開するためにWebサービスへの別の呼び出しが行われると、ディレクトリが存在しなくなったことが報告され、プログラムコードは、おそらく欠落しているdirを作成しようとし、例外メッセージSystemで失敗します。 UnauthorizedAccessException:パス'\\ nasshare\test'へのアクセスが拒否されました。
奇妙なことに、Webサーバーを再起動すると、プロセスが終了するまで再び機能します。Webサービスによって作成された後続のプロセスは失敗します。Webサーバーのコマンドプロンプトからプロセスを実行すると、プロセスはフォルダーを確認して書き込むことができます。この方法でプロセスを何度も実行でき、Webサーバー内でCreateProcessAsUserを使用して起動すると、失敗した直後でも正常に機能します。
- Webサーバー:Windows Server 2008 R2
- ネットワーク共有:Windows Storage Server 2003
- Webサービスホスティング:IIS7
- Webサービスクライアント:IE、Chrome
- アプリケーションプールID:NetworkService
- .Net Framework:v4.0
簡略化されたRESTWebサービス
[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;
}
test.exeプログラムコードの簡略化した例
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");
}
}