Win7(32bit)でUACを触ってみたメモ。
Administratorsグループに所属するユーザでログインし、コマンドプロンプトを2つ立ち上げ、ただし片方は「管理者として実行」していろいろ試してみました。
未来の自分向けであるのは勿論ですが、UACと付き合うときに試行錯誤しつつ作るであろう実験コードのサンプルになれば幸いです。
例によりMSDNのURLについては2010-11-12時点でのURLです。将来変更された場合は、併記しているナビゲータを参考に辿り直してください。題名が長くて特徴的なMSDN記事については、検索エンジンでたどれると思いますのでナビゲーションは併記していません。
技術/Windows/UACとRunAsについてのメモ でも紹介している、@ITの解説記事をまずお勧めします。
MSDN提供のドキュメントを参照する場合は、次の3本が軽めですのでまずはこちらでウォーミングアップ。
グループポリシーを使ったUACの微調整や、企業内での独自アプリ・インストーラの配布、アプリケーションの修正など、本格的にUACとお付き合いする場合は以下の2本でMicrosoftお勧めのお作法を学べます。その分、記事の分量もそれなりにあります。
オプション:ShellExecute(Ex)()の内部を解析して、"CreateProcessElevated()"なるAPIをメインとしたユーティリティライブラリを作った人が、その解説記事を書いてます。
"MSDN" > "MSDN Library" > "Windows Development" > "Security" > "Authorization" > ...
Web/DB プログラミング徹底解説シリーズ:
"Create process nonelevated from elevated process" でggったら一発でした。
OpenProcessToken()で現在プロセスのTokenハンドルを取得し、GetTokenInformation()で権限昇格(Elevation)の前後で内容が変わりそうな情報を取得、表示させてみます。
TOKEN_ELEVATION構造体のTokenIsElevatedに権限昇格時は1, 未昇格時は0がセットされるようです。
通常のコマンドプロンプト:
> TokenElevation TOKEN_ELEVATION = 0
「管理者として実行」コマンドプロンプト:
> TokenElevation TOKEN_ELEVATION = 1
UACを無効にしてAdministratorsグループに所属するユーザーでログインしたコマンドプロンプト:
> TokenElevation TOKEN_ELEVATION = 1
現在の権限昇格種別が取得できるみたいです。
通常のコマンドプロンプト:
> TokenElevationType TOKEN_ELEVATION_TYPE = 3, TokenElevationTypeLimited
「管理者として実行」コマンドプロンプト:
> TokenElevationType TOKEN_ELEVATION_TYPE = 2, TokenElevationTypeFull
UACを無効にしてAdministratorsグループに所属するユーザーでログインしたコマンドプロンプト:
> TokenElevationType TOKEN_ELEVATION_TYPE = 1, TokenElevationTypeDefault
与えられたPrivilegeの一覧を表示してみます。権限昇格前後での違いが一番良くわかる情報です。
通常のコマンドプロンプト:
> TokenPrivileges.exe Privileges[0]: PrivilegeName = [SeShutdownPrivilege] PrivilegeAttributes: Privileges[1]: PrivilegeName = [SeChangeNotifyPrivilege] PrivilegeAttributes: SE_PRIVILEGE_ENABLED SE_PRIVILEGE_ENABLED_BY_DEFAULT SE_PRIVILEGE_REMOVED SE_PRIVILEGE_USED_FOR_ACCESS Privileges[2]: PrivilegeName = [SeUndockPrivilege] PrivilegeAttributes: Privileges[3]: PrivilegeName = [SeIncreaseWorkingSetPrivilege] PrivilegeAttributes: Privileges[4]: PrivilegeName = [SeTimeZonePrivilege] PrivilegeAttributes:
「管理者として実行」コマンドプロンプト:
> TokenPrivileges.exe Privileges[0]: PrivilegeName = [SeIncreaseQuotaPrivilege] PrivilegeAttributes: Privileges[1]: PrivilegeName = [SeSecurityPrivilege] PrivilegeAttributes: Privileges[2]: PrivilegeName = [SeTakeOwnershipPrivilege] PrivilegeAttributes: Privileges[3]: PrivilegeName = [SeLoadDriverPrivilege] PrivilegeAttributes: Privileges[4]: PrivilegeName = [SeSystemProfilePrivilege] PrivilegeAttributes: Privileges[5]: PrivilegeName = [SeSystemtimePrivilege] PrivilegeAttributes: Privileges[6]: PrivilegeName = [SeProfileSingleProcessPrivilege] PrivilegeAttributes: Privileges[7]: PrivilegeName = [SeIncreaseBasePriorityPrivilege] PrivilegeAttributes: Privileges[8]: PrivilegeName = [SeCreatePagefilePrivilege] PrivilegeAttributes: Privileges[9]: PrivilegeName = [SeBackupPrivilege] PrivilegeAttributes: Privileges[10]: PrivilegeName = [SeRestorePrivilege] PrivilegeAttributes: Privileges[11]: PrivilegeName = [SeShutdownPrivilege] PrivilegeAttributes: Privileges[12]: PrivilegeName = [SeDebugPrivilege] PrivilegeAttributes: Privileges[13]: PrivilegeName = [SeSystemEnvironmentPrivilege] PrivilegeAttributes: Privileges[14]: PrivilegeName = [SeChangeNotifyPrivilege] PrivilegeAttributes: SE_PRIVILEGE_ENABLED SE_PRIVILEGE_ENABLED_BY_DEFAULT SE_PRIVILEGE_REMOVED SE_PRIVILEGE_USED_FOR_ACCESS Privileges[15]: PrivilegeName = [SeRemoteShutdownPrivilege] PrivilegeAttributes: Privileges[16]: PrivilegeName = [SeUndockPrivilege] PrivilegeAttributes: Privileges[17]: PrivilegeName = [SeManageVolumePrivilege] PrivilegeAttributes: Privileges[18]: PrivilegeName = [SeImpersonatePrivilege] PrivilegeAttributes: SE_PRIVILEGE_ENABLED SE_PRIVILEGE_ENABLED_BY_DEFAULT SE_PRIVILEGE_REMOVED SE_PRIVILEGE_USED_FOR_ACCESS Privileges[19]: PrivilegeName = [SeCreateGlobalPrivilege] PrivilegeAttributes: SE_PRIVILEGE_ENABLED SE_PRIVILEGE_ENABLED_BY_DEFAULT SE_PRIVILEGE_REMOVED SE_PRIVILEGE_USED_FOR_ACCESS Privileges[20]: PrivilegeName = [SeIncreaseWorkingSetPrivilege] PrivilegeAttributes: Privileges[21]: PrivilegeName = [SeTimeZonePrivilege] PrivilegeAttributes: Privileges[22]: PrivilegeName = [SeCreateSymbolicLinkPrivilege] PrivilegeAttributes:
"Integrity Level"とかいうのに関連してるようですが、まだ理解し切れていません。
通常のコマンドプロンプト:
> TokenMandatoryPolicy.exe TOKEN_MANDATORY_POLICY = 3, TOKEN_MANDATORY_POLICY_VALID_MASK
「管理者として実行」コマンドプロンプト:
> TokenMandatoryPolicy.exe TOKEN_MANDATORY_POLICY = 1, TOKEN_MANDATORY_POLICY_NO_WRITE_UP
TokenMandatoryPolicyと併せて "Integrity Level" をあらわすSIDを取得できるようですが、まだ理解し切れていません。また、リソースの解放で間違いがあるのか、異常終了してしまいます。情けない話ですが、追いきれませんでした。
通常のコマンドプロンプト:
> TokenMandatoryLabel.exe MandatoryLabel : SID = S-1-16-8192 Attribute = 00000060
「管理者として実行」コマンドプロンプト:
> TokenMandatoryLabel.exe MandatoryLabel : SID = S-1-16-12288 Attribute = 00000060
表示されているSID自体は、MSDNの"Windows Integrity Mechanism Design"に記載されている内容と一致しています。
「管理者として実行」側の "S-1-16-12288" は "Mandatory Label\High Mandatory Level"、
通常側の "S-1-16-8192" は "Mandatory Label\Medium Mandatory Level" になりますので、他のUACドキュメントの説明どおりです。
Administratorsグループのユーザがログオンするとき、管理者としてのフルアクセス用トークンと通常ユーザとしての制限されたトークンが発行されます。この二つのトークンで、現在プロセスのトークンと対になるトークンがTokenLinkedTokenで取得できます。
通常のコマンドプロンプト:現在プロセスのTokenは制限されたトークンですので、LinkedTokenには対となる、フルアクセス用トークンが割り当てられています。
> TokenLinkedToken.exe --------------- CURRENT TOKEN -------------------- Privileges[0]: PrivilegeName = [SeShutdownPrivilege] PrivilegeAttributes: (省略) Privileges[4]: PrivilegeName = [SeTimeZonePrivilege] PrivilegeAttributes: --------------- LINKED TOKEN -------------------- Privileges[0]: PrivilegeName = [SeIncreaseQuotaPrivilege] PrivilegeAttributes: (省略) Privileges[22]: PrivilegeName = [SeCreateSymbolicLinkPrivilege] PrivilegeAttributes:
「管理者として実行」コマンドプロンプト:現在プロセスのTokenはフルアクセス用トークンですので、LinkedTokenには対となる、制限されたトークンが割り当てられています。
> TokenLinkedToken.exe --------------- CURRENT TOKEN -------------------- Privileges[0]: PrivilegeName = [SeIncreaseQuotaPrivilege] PrivilegeAttributes: (省略) Privileges[22]: PrivilegeName = [SeCreateSymbolicLinkPrivilege] PrivilegeAttributes: --------------- LINKED TOKEN -------------------- Privileges[0]: PrivilegeName = [SeShutdownPrivilege] PrivilegeAttributes: (省略) Privileges[4]: PrivilegeName = [SeTimeZonePrivilege] PrivilegeAttributes:
UACを無効にしてAdministratorsグループに所属するユーザーでログインしたコマンドプロンプト:
UACを無効にすると、制限トークンが発行されなくなります。したがってLinkedTokenは存在せず、管理者グループのユーザでログオンした場合はフルアクセス用トークンのみが発行されます。
> TokenLinkedToken.exe --------------- CURRENT TOKEN -------------------- Privileges[0]: PrivilegeName = [SeIncreaseQuotaPrivilege] PrivilegeAttributes: (省略) Privileges[22]: PrivilegeName = [SeCreateSymbolicLinkPrivilege] PrivilegeAttributes: GetTokenInformation() : 1312
最後のGetLastError() = 1312はLinkedTokenのGetTokenInformation()で発生しています。日本語メッセージは以下になります。
指定されたログオン セッションは存在しません。\ そのセッションは既に終了している可能性があります。
未昇格→未昇格プロセスのCreateProcess() : OK
未昇格→昇格プロセスのCreateProcess() : ERROR_ELEVATION_REQUIRED、ShellExecute(Ex)()を使う。
昇格→昇格プロセスのCreateProcess() : OK
では、
昇格→未昇格プロセスのCreateProcess()
するにはどうすれば良いでしょうか?
以上、実験コードの足しになれば幸いです。
それにしてもWindowsのアクセス制御は複雑ですね。企業内で何百台ものクライアントPCを適切に管理したり、GUIのDesktopシステムとの組み合わせが発生する以上は仕方ないのかもしれません。