#navi_header|技術| Win7(32bit)でUACを触ってみたメモ。 Administratorsグループに所属するユーザでログインし、コマンドプロンプトを2つ立ち上げ、ただし片方は「管理者として実行」していろいろ試してみました。 未来の自分向けであるのは勿論ですが、UACと付き合うときに試行錯誤しつつ作るであろう実験コードのサンプルになれば幸いです。 #more|| #outline|| ---- * 参考URL 例によりMSDNのURLについては2010-11-12時点でのURLです。将来変更された場合は、併記しているナビゲータを参考に辿り直してください。題名が長くて特徴的なMSDN記事については、検索エンジンでたどれると思いますのでナビゲーションは併記していません。 ** UACそのものについての参考URL: [[829]] でも紹介している、@ITの解説記事をまずお勧めします。 MSDN提供のドキュメントを参照する場合は、次の3本が軽めですのでまずはこちらでウォーミングアップ。 - UAC Processes and Interactions (TechNet, Windows 7, Security and Protection, How UAC Works) -- http://technet.microsoft.com/en-us/library/dd835561%28WS.10%29.aspx - UAC Architecture (TechNet, Windows 7, Security and Protection, How UAC Works) -- http://technet.microsoft.com/en-us/library/dd835540%28WS.10%29.aspx - Windows Vista Application Development Requirements for User Account Control (UAC) -- http://msdn.microsoft.com/en-us/library/aa905330.aspx グループポリシーを使ったUACの微調整や、企業内での独自アプリ・インストーラの配布、アプリケーションの修正など、本格的にUACとお付き合いする場合は以下の2本でMicrosoftお勧めのお作法を学べます。その分、記事の分量もそれなりにあります。 - Windows Vista Application Development Requirements for User Account Control Compatibility -- http://msdn.microsoft.com/en-us/library/bb530410.aspx - Understanding and Configuring User Account Control in Windows Vista -- http://technet.microsoft.com/en-us/library/cc709628%28WS.10%29.aspx オプション:ShellExecute(Ex)()の内部を解析して、"CreateProcessElevated()"なるAPIをメインとしたユーティリティライブラリを作った人が、その解説記事を書いてます。 - Vista UAC: The Definitive Guide - CodeProject -- http://www.codeproject.com/KB/vista-security/UAC__The_Definitive_Guide.aspx ** 前半「とりあえず権限昇格する前後のTOKENをいくつか見てみる」の参考URL: "MSDN" > "MSDN Library" > "Windows Development" > "Security" > "Authorization" > ... - "About Authorization" > "Access Control" > "Access Control Model" > "Parts of the Access Control Model" > "Access Tokens" -- http://msdn.microsoft.com/en-us/library/aa374909%28VS.85%29.aspx -- ... > "Access Rights for Access-Token Objects" --- http://msdn.microsoft.com/en-us/library/aa374905%28VS.85%29.aspx - "Authorization Reference" > "Authorization Functions" > "GetTokenInformation Function" -- http://msdn.microsoft.com/en-us/library/aa446671%28VS.85%29.aspx - "Using Authorization in C++" > "Supporting Tasks for Authorization in C++" > "Establishing a Client Context from a SID in C++" > "Searching for a SID in an Access Token in C++" -- http://msdn.microsoft.com/en-us/library/aa379554%28VS.85%29.aspx - "MSDN" > "MSDN Library" > ".NET Development" > "Articles and Overviews" > "Windows Vista" > "Technical Articles" > "Windows Vista Integrity Mechanism Technical Reference" > "Windows Integrity Mechanism Design" -- http://msdn.microsoft.com/en-us/library/bb625963.aspx Web/DB プログラミング徹底解説シリーズ: - UAC (ユーザーアクセスコントロール) と管理権限の昇格 (elevating) -- http://keicode.com/windows/uac-elevating-to-admin.php - 管理者権限での実行を要求する方法 -- http://keicode.com/windows/programming-uac-elevating-to-admin.php - Vista 以降におけるトークンの変更 ~ リンクト・トークン (フィルタード・トークン) -- http://keicode.com/windows/linked-token.php ** 後半「権限昇格したプロセスから、未昇格状態のプロセスを起動する」の参考URL: "Create process nonelevated from elevated process" でggったら一発でした。 - EternalWindows : セキュリティコンテキスト / UAC -- http://eternalwindows.jp/security/securitycontext/securitycontext10.html - Create non-elevated process from elevated process,...? in VC MFC -- http://www.eggheadcafe.com/software/aspnet/35797180/create-nonelevated-process-from-elevated-process.aspx - FAQ: How do I start a program as the desktop user from an elevated app? - Aaron Margosis' "Non-Admin" and App-Compat WebLog - Site Home - MSDN Blogs -- http://blogs.msdn.com/b/aaron_margosis/archive/2009/06/06/faq-how-do-i-start-a-program-as-the-desktop-user-from-an-elevated-app.aspx ---- * とりあえず権限昇格する前後のTOKENをいくつか見てみる OpenProcessToken()で現在プロセスのTokenハンドルを取得し、GetTokenInformation()で権限昇格(Elevation)の前後で内容が変わりそうな情報を取得、表示させてみます。 ** TokenElevation TOKEN_ELEVATION構造体のTokenIsElevatedに権限昇格時は1, 未昇格時は0がセットされるようです。 - [[yb://medias/exercise_uac/t_get_token_informations/TokenElevation.c]] 通常のコマンドプロンプト: > TokenElevation TOKEN_ELEVATION = 0 「管理者として実行」コマンドプロンプト: > TokenElevation TOKEN_ELEVATION = 1 UACを無効にしてAdministratorsグループに所属するユーザーでログインしたコマンドプロンプト: > TokenElevation TOKEN_ELEVATION = 1 ** TokenElevationType 現在の権限昇格種別が取得できるみたいです。 - [[yb://medias/exercise_uac/t_get_token_informations/TokenElevationType.c]] 通常のコマンドプロンプト: > TokenElevationType TOKEN_ELEVATION_TYPE = 3, TokenElevationTypeLimited 「管理者として実行」コマンドプロンプト: > TokenElevationType TOKEN_ELEVATION_TYPE = 2, TokenElevationTypeFull UACを無効にしてAdministratorsグループに所属するユーザーでログインしたコマンドプロンプト: > TokenElevationType TOKEN_ELEVATION_TYPE = 1, TokenElevationTypeDefault ** TokenPrivileges 与えられたPrivilegeの一覧を表示してみます。権限昇格前後での違いが一番良くわかる情報です。 - [[yb://medias/exercise_uac/t_get_token_informations/TokenPrivileges.c]] 通常のコマンドプロンプト: #pre||> > 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: ||< 「管理者として実行」コマンドプロンプト: #pre||> > 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: ||< ** TokenMandatoryPolicy "Integrity Level"とかいうのに関連してるようですが、まだ理解し切れていません。 - [[yb://medias/exercise_uac/t_get_token_informations/TokenMandatoryPolicy.c]] 通常のコマンドプロンプト: > TokenMandatoryPolicy.exe TOKEN_MANDATORY_POLICY = 3, TOKEN_MANDATORY_POLICY_VALID_MASK 「管理者として実行」コマンドプロンプト: > TokenMandatoryPolicy.exe TOKEN_MANDATORY_POLICY = 1, TOKEN_MANDATORY_POLICY_NO_WRITE_UP ** TokenIntegrityLevel TokenMandatoryPolicyと併せて "Integrity Level" をあらわすSIDを取得できるようですが、まだ理解し切れていません。また、リソースの解放で間違いがあるのか、異常終了してしまいます。情けない話ですが、追いきれませんでした。 - [[yb://medias/exercise_uac/t_get_token_informations/TokenMandatoryLabel.c]] 通常のコマンドプロンプト: > 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ドキュメントの説明どおりです。 ** TokenLinkedToken Administratorsグループのユーザがログオンするとき、管理者としてのフルアクセス用トークンと通常ユーザとしての制限されたトークンが発行されます。この二つのトークンで、現在プロセスのトークンと対になるトークンがTokenLinkedTokenで取得できます。 - [[yb://medias/exercise_uac/t_get_token_informations/TokenLinkedToken.c]] 通常のコマンドプロンプト:現在プロセスのTokenは制限されたトークンですので、LinkedTokenには対となる、フルアクセス用トークンが割り当てられています。 #pre||> > 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には対となる、制限されたトークンが割り当てられています。 #pre||> > 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は存在せず、管理者グループのユーザでログオンした場合はフルアクセス用トークンのみが発行されます。 #pre||> > 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() するにはどうすれば良いでしょうか? ** まずは実験用のCreateProcess()される側のプログラムを用意する。 - [[yb://medias/exercise_uac/t_uac_with_manifest/check_elevation.c]] - 親プロセスと同じトークンで起動するためのmanifest -- [[yb://medias/exercise_uac/t_uac_with_manifest/check_elevation.exe_asInvoker.manifest]] - 権限昇格を強制するためのmanifest -- [[yb://medias/exercise_uac/t_uac_with_manifest/check_elevation.exe_requireAdministrator.manifest]] ** セオリー通りにShellExecuteEx()で起動してみる - [[yb://medias/exercise_uac/t_uac_with_manifest/t_shell_execute_ex.c]] - 通常コマンドプロンプト -- asInvoker(manifest) → TOKEN_ELEVATION = 0 -- requireAdministrator(manifest) → UACプロンプト + TOKEN_ELEVATION = 1 - 「管理者として実行」コマンドプロンプト -- asInvoker(manifest) → TOKEN_ELEVATION = 1 -- requireAdministrator(manifest) → TOKEN_ELEVATION = 1 ** 特に細工無しのCreateProcess()で起動してみる - [[yb://medias/exercise_uac/t_uac_with_manifest/t_create_process.c]] - 通常コマンドプロンプト -- asInvoker(manifest) → TOKEN_ELEVATION = 0 -- requireAdministrator(manifest) → ERROR_ELEVATION_REQUIRED(GetLastError() = 740) - 「管理者として実行」コマンドプロンプト -- asInvoker(manifest) → TOKEN_ELEVATION = 1 -- requireAdministrator(manifest) → TOKEN_ELEVATION = 1 ** MSDN Blogsの内容に従い、デスクトップユーザーのTokenをコピーしてCreateProcessWithTokenW()に渡してみる - [[yb://medias/exercise_uac/t_uac_with_manifest/t_create_process_as_desktop_user.c]] - 通常コマンドプロンプト:(権限昇格済みのプロセスからのCreateProcess()実験に付き対象外) - 「管理者として実行」コマンドプロンプト -- asInvoker(manifest) → ''TOKEN_ELEVATION = 0'' -- requireAdministrator(manifest) → ''ERROR_ELEVATION_REQUIRED(GetLastError() = 740)(CreateProcessWithTokenW())'' ---- 以上、実験コードの足しになれば幸いです。 それにしてもWindowsのアクセス制御は複雑ですね。企業内で何百台ものクライアントPCを適切に管理したり、GUIのDesktopシステムとの組み合わせが発生する以上は仕方ないのかもしれません。 #navi_footer|技術|