Windows
C#
.NET

Systemユーザーで動作しているサービスからの共有フォルダの認証を行う(C# .NET)

More than 3 years have passed since last update.


はじめに

作成していたプログラムで、Systemユーザーで動作しているWindowsサービスから共有フォルダの認証を行いファイルのコピーを行う処理ではまったためメモとして残します。


共有フォルダの認証


まずは認証

WNetAddConnection2()で認証してWNetCancelConnection2()で認証解除を行う。

Win32APIなので、DllImport属性を使用してマネージドコードから呼び出せるようにする必要がある。


結果

通常ユーザー(Windowsにログオンする時に使用するユーザー)のプロセスで実行した場合には成功する。

しかし、System(Local System)ユーザーで動作するWindowsサービスから実行するとWNetAddConnection2()の呼び出しでエラーコード:1312となって失敗する。必ず失敗する訳ではなく、一度目は成功して二度目以降は失敗、しばらくするとまた成功するがそれ以降失敗となる動作が続く。


問題点

Systemユーザーはなんやかんやで共有フォルダーへの認証が使用出来ない。(っぽい。いろいろググった結果。なんかネットワーク資源へのアクセス許可がないとかだった気がする…)


解決策


  • Systemユーザーではなく、NetworkServiceユーザーでプロセスを起動する。

  • 当該処理実行時にユーザー偽装を行う。

Systemユーザーで動作する必要があったので、ユーザー偽装を行う事とする。


ユーザー偽装

認証と認証解除の処理を行う際にユーザーの偽装を行う。

(そんな機能があったことを初めて知りました…勉強が足りないですね…)

ユーザー偽装の処理の流れ



  1. LogonUser()で偽装先のユーザー(今回はNetworkServiceユーザー)のトークンを取得


  2. WindowsIdentityクラスのスタティックメソッドImpersonate()で1で取得したトークンを使用して1で指定したユーザに偽装(ここから、偽装を解くまでは1で指定したユーザで処理が実行される)

  3. 偽装中に行いたい処理を実行(今回の場合はWNetAddConnection2()、認証中に行う処理、WNetCancelConnection2()を実行)

  4. 2のImpersonate()の戻り値として取得したWindowsImpersonationContextクラスのインスタンスのUndo()で偽装を解除


実装

Stack Overflow : How to use LogonUser properly to impersonate domain user from workgroup client」のJJ_Coder4Hire氏の回答を参考にした。

このコードはユーザー偽装を行うコードであり、今回の目的の「共有フォルダー認証を行う」ために以下の設定でLogonUser()を呼び出した。


  • user : "NetworkService"

  • domain : "NT AUTHORITY"

  • password : ""

  • logonType : LOGON32_LOGON_NEW_CREDENTIALS

  • logonProvider : LOGON32_PROVIDER_DEFAULT

NetworkServiceユーザに偽装するための指定は「Windows SDK Support Team Blog : How to programmatically create a LogonService or Network Service token with LogonUser?」を参考にした。

LogonTypeも何らかのサイトを参考にしたが失念。


結果

WNetAddConnection2も動作し、正常に共有フォルダーへのファイルのコピーが出来た。


最後に

とりあえず目的の処理を実装させる事は出来た。けれどもなぜ動いているのか完全に理解できないため気持ちが悪い。(logonTypeをLOGON32_LOGON_SERVICEにすると動かないとか)

ちゃんとドキュメント読まないとなぁ

いまいち理解出来ていない点があるため、間違っている点がありましたら指摘願います。