Windowsサーバのフォルダアクセス権限(DACL)を再帰的に取得する

イントロダクション

あるファイルサーバの運用・管理に関わっている。別に本業ではないし*1、サーバ管理を本職としていた経験も皆無なのだが、基本的に社内に常駐している人なので都合が良いのだろう。まあ中小企業の社内システム管理なんて、(直接的に利益が発生する作業ではないので)私のような兼業状態の人が大半ではないだろうか。

こういう場合、サーバ管理の作業を何とか効率化したくなる訳で、

  1. マニュアル化を進めて、他の人でも何とか作業できるようにする。
  2. 手作業の部分を何とか自動化してみる。

こんな方向に話が進む。今回は2番目の話。

Windows ServerなOSでファイルサーバを運用していて、公開フォルダ内ではアクセス権を色々と設定している。今までは各フォルダごとのアクセス権限の一覧を手作業でメンテナンスしていたのだが、ハッキリ言って非常に面倒だ。それにどこかでミスして現状と一致していないとも限らない。

そこで、各フォルダごとのアクセス権限の一覧を自動生成するスクリプトを書くことにした。というかここで自動化せずして、何を自動化するというのか?

ちなみにこの作業、個人的にヤル気になったものの優先度は低くて、会社で作業する訳にはいかなかった。そこで自宅で個人プロジェクトとして進めることにした。成果物はフリーなライセンスで公開している。

必要な要件

  • フォルダのアクセス権限(DACL)の情報を可視化する。
    • ファイルは含まない。
    • 可視化の形式は、フォルダのプロパティからセキュリティの詳細設定を表示させた時のアクセス許可エントリの表示に準拠。
  • 指定したフォルダ以下、再帰的に情報を取得する。
    • 再帰のレベル(何段下の階層まで情報を取得するか?)を指定可能とする。
      • 指定しなかったら、最下層まで取得する。
  • 継承されたアクセス権限の場合は、継承元となったフォルダのパスも取得する。
    • そうでなければ「継承なし」とする。
  • 取得した情報はCSV形式で出力する。
  • 非対話型のスクリプトとして実装する。
    • CSVの出力は単純に標準出力に垂れ流す。ファイルに出力したい場合はリダイレクトで。
  • サーバマシン上で追加コンポーネント無しで動作する。
  • 処理速度にはあまり拘らない。無茶苦茶遅すぎるとかでなければOK。

単純に指定したフォルダのDACLを表示する

フォルダのDACLを表示するVBScriptはネット上で簡単に見つけられる。本家本元MicrosoftTechnetでも公開されていたりする。

http://www.microsoft.com/japan/technet/scriptcenter/scripts/security/dacls/sedcvb01.mspx

但し、このままではお世辞にも表示内容が分かりやすいとは言えない。もう一加工必要だ。

DACLの情報はWMIのWin32_ACEクラスWin32_Trusteeクラスのオブジェクトのプロパティから取得するのだが、この内容は例えばフォルダのプロパティのセキュリティタブなどで表示される情報と一対一で対応している訳ではない。なので「こう表示される時は、オブジェクトのプロパティの値はこうなっている」といった具合に地道に調べて、現物あわせで進めていくことになる。

例えばアクセス許可エントリでは、

  • フルコントロール
  • 読み取り
  • 読み取りと実行

などといった具合にアクセス権の概要が表示される。これに該当する情報はWin32_ACE::AccessMaskというuint32型のプロパティで取得できるのだが、このプロパティがどの値の時に「フルコントロール」となり、どの値の時に「読み取りと実行」となるかについては、恐らく地道にフォルダのDACLを調べていかないと分からないのではないだろうか? 少なくとも私は、参考になる資料を見つけられなかったので地道に調べることにした。

再帰的に情報を取得する

今こそ id:eel3:20091026:1256558649 で書いた雛形の出番だ。という訳で使用言語はVBScriptではなくJScriptに決定。

ただ今回は、再帰の上限回数を指定して処理を打ち切ることができるように改造する必要がある。

ちなみにこの雛形では本当に再帰している(再帰処理で実装している)のだが、基本的にフォルダはツリー上になっているので*2再帰が無茶苦茶深くなる可能性は低めだと推測できる。なのでそのままにしておくことにした。

継承元フォルダを探す

MSDNを読む限り、Win32_ACEクラスやWin32_Trusteeクラスなどには継承元フォルダに関する情報は含まれていない。Win32_ACE::AceFlagsを調べることでどこかから継承されたDACLか否かは分かるのだが*3、肝心の継承元は不明だ。

いったいどうやって継承元フォルダを特定しているのだろうか? 正しい方法は不明だが、参考になりそうなネタは見つかった。

VBScriptで、フォルダのアクセス権におけるユーザーの継承元を調… - 人力検索はてな

今回はこのページの情報を参考とすることにした。但し、毎回再帰的に上位フォルダのDACLを取得するのは効率的とは言えない。そこで基本的なアルゴリズムはそのままに、継承元となっている可能性が高い上位フォルダのDACLをスタックにキャッシュする構成で実装することにした。

全部まとめると――関数enumFolderDACL()

以上の内容をまとめて実装したのが、関数 enumFolderDACL() だ。

/// 指定したフォルダ folder 以下のDACLを再帰的に表示する。
/// recursionMaxには、再帰する階層数を設定する。
///   recursionMax == 0 : folderのDACLのみ表示する。
///   recursionMax >= 1 : folder以下の、指定した階層分だけ表示する。
///   何も考えず最下層まで表示したい場合は、何も設定しないこと。
var enumFolderDACL = function(folder, recursionMax)

とりあえずインターフェイス部分のみ。ソースは「enumFolderDACL() + テスト用のメインルーチン」の構成で、以下のページで公開している。

フォルダのアクセス権限を再帰的に表示するJScript関数&テスト用メインルーチン

一つのツールとして完成している訳ではないので注意。実際、私がサーバ管理に使おうとしているバージョンでは、enumFolderDACL() はそのままだがメインルーチン部分は書き換えてある。

実装自体も未完成な部分が幾つかある。Win32_ACE::AceFlagsやWin32_ACE::AccessMaskの値からアクセス権の表示形式に変換する部分は、私が使う時に最低限必要な分しか実装してない。世の中の他のサーバで使用するには不足しているはずだ。

またCREATE_OWNERのアクセス権限の表示内容は全くもって正しくないのだが、私が使う分には放置していても問題ないので、対応していない。

おまけの雑感

  • JScript最高。クロージャとか無名関数とか、非常に助かる。
  • そういえばJScriptで初めてクラス(?)を実装した気がする。
  • あとデータ構造として明示的にスタックを使ったのも初めて*4

*1:基本的に開発の仕事が本業で、サーバ管理は副業といった所。

*2:Unixの世界の言葉で「ディレクトリツリー」なんてものがあるくらいだし。

*3:Win32_ACE::AceFlagsと定数INHERITED_ACEをANDでビットマスクを取る。ビットが立っていたらどこかから継承されたDACLと判断できる。

*4:コールスタックにはいつもお世話になってるけど。