最近サーバー市場で目まぐるしくシェアを伸ばしているのが Linux( PC-UNIX ) です。
開発者の皆さんの中にも,クライアントに Windows マシン,サーバーに Linux やオフコンなどを使用したマルチプラットフォームで開発されている方も多いかと思います。
※ これからの開発ツールは マルチ環境,マルチ階層の配慮が求められてきています。
OS に特化した開発ツールより,より分散環境を考慮された開発ツールを選ばれるのが賢明に思われます。
ところで Unixでは,ユーザーごとにセキュリティを設けユーザーによって可能な処理と不可能な処理を使い分けることが可能です。
Windows NT でも同じような設定が可能ですが,Unix 系の OS ではログイン中に別のユーザーに成り済ます事が可能です。
この処理を対話的に実現しているのが su コマンドです。
Windows NT でも同じ処理ができないのでしょうか。
今回は Windows NT による Substitute User( 偽装ユーザー ) を C++Builder のプログラミングで実現したいと思います。
1. Substitute User
su コマンドは,あるユーザーでログイン中に別のユーザーに成り代わって,特定の処理を行いたい時に使います。
シャットダウン権限のないユーザーでログインして,あるアプリケーションを実行中にマシンのシャットダウンを行う時有効です。
( Unix では,あるアプリケーションの環境を設定した後,リブートを行う必要はほとんどありませんが,Windows の世界では頻繁に発生します。)
※ WindowsNT の世界では,ログインすることをログオンと呼んでいます。
これ以降,ログインをログオンと表記します。
ユーザーの変更に LogonUser を使用し,そのプロセスの実行に CreateProcessAsUser を使用します。
1.1 LogonUser
この関数は,指定したユーザーでドメインにログオンすることができます。
CreateProcessAsUser を呼び出すためのハンドルを受け取ることができます。
( LogonUser のログオンは WindowsNT のツール,イベントビューアで確認できます。 )
関数の型,引数は次のようになっています。
BOOL
LogonUser(
LPTSTR lpszUserName,
LPTSTR lpszDomain,
LPTSTR lpszPassword,
DWORD dwLogonType,
DWORD dwLogonProvider,
PHANDLE phToken
)
lpszUserName:
ログオンするユーザー名
lpszDomain:
ログオンするドメイン名
lpszPassword:
ログオンするユーザーのパスワード
dwLogonType:
ログオンオペレーションのタイプ
dwLogonProvider:
ログオンプロバイダ
phToken:
トークンハンドルのポインタ
この関数を実行するユーザーには,ユーザーマネージャを使用してSE_TCB_NAME 権限を与えておく必要があります。
※ 詳しくは Windows SDKオンラインヘルプを参照
1.2 CreateProcessAsUser
この関数は,トークンハンドルに結びつけられたユーザーで新しいプロセスとメインスレッドを作ります。
引数にトークンハンドルを指定する以外は,CreateProcess 関数と同じです。
関数の型,引数は次のようになっています。
BOOL
CreateProcessAsUser(
HANDLE hToken,
LPCSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL
bInhertHandles,
DWORD dwCreationFlags,
LPVOID lpCurrentDirectory
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
hToken
トークンハンドル
lpApplicationName,
アプリケーション名
lpCommandLine,
コマンドライン
lpProcessAttributes,
プロセスのセキュリティ属性
lpThreadAttributes,
スレッドのセキュリティ属性
bInhertHandles,
プロセスの継承
dwCreationFlags,
作成フラグ
lpCurrentDirectory
プロセスの環境ブロック
lpCurrentDirectory,
作成されるプロセスの作業ディレクトリ
lpStartupInfo,
STARTUPINFO構造体
lpProcessInformation
PROCESS_INFORMATION構造体
ユーザーマネージャを使用して,この関数を実行するユーザーにSE_ASSIGNPRIMARYTOKEN_NAME 権限とSE_INCREASE_QUOTA_NAME権限を与えておく必要があります。
※ 詳しくは Windows SDKオンラインヘルプを参照
1.3 WindowsNT のウィンドウ管理オブジェクト
WindowsNT は,オブジェクトを階層的に管理しています。
上記の様に Windos NT の世界では階層的にオブジェクトを管理しそのオブジェクトごとにセキュリティが設けられています。
別のユーザーの Window を DeskTop や WindowStation で実行できるようにするために,これらのオブジェクトにアクセス権を与える必要があります。
ここで「 Unix では,ウィンドウにセキュリティなど無いのにWindows NT では手間がかかってしまうなぁ」と疑問を抱いた方もおられるかと思います。
そうです。Unix の X-Window は Unix( OS ) ではありません。Windows NT の Window は Windows NT( OS ) そのものなのです。
そこで LogonUser でユーザーを偽装した後,偽装したユーザーが作成元ユーザーのWindowStation と Desktop にアクセスできるようにセキュリティを開放します。
開放させるためにセキュリティAPI の InitializeSecurityDescriptor, SetSecurityDescriptorDacl,SetUserObjectSecurity を使用します。
※ セキュリティAPIに関しては,Windows SDKオンラインヘルプ,または,関連書籍を参考にして下さい。
2. セキュリティ
セキュリティAPI を使用する前に Windows NT のセキュリティに関しての概要を説明します。
各ユーザーは,アクセストークンを保持しています。
アクセストークンの中には,ユーザーのセキュリティ識別子,グループのセキュリティ識別子特権などを保持しています。ログオン後は,このアクセストークンを使用して認証を行っています。
一方オブジェクト側にもセキュリティ情報を保持しています。セキュリティ記述子です。
その中は,所有者セキュリティ識別子,グループセキュリティ識別子,随意アクセス制御リストシステムアクセス制御リストです。さらにアクセス制御リストは,アクセス制御エントリを保持しており,セキュリティ識別子のアクセスマスクを管理しています。
2.1 用語解説
セキュリティプログラミングで使用される用語の日本語対応表です。
SID |
セキュリティ識別子 |
Privilege |
特権 |
SecurityDescriptor |
セキュリティ記述子 |
DACL |
随意アクセス制御リスト |
SACL |
システムアクセス制御リスト |
ACE |
アクセス制御エントリ |
|
|
3. プログラミング
それぞれのユニットに関して説明します。
Unit1 では,TSubstitute クラスを使用して Desktop と WindowStation のアクセスマスクを追加して,コマンドを実行し追加したパーミッションを削除しています。
Unit2 は,Unit3 と Unit4 の基底クラス TWinObject を定義しています。
このクラスは GetErrorMsg 関数を Unit3 や Unit4 で利用できるようにC++ 風にしました。
Unit3 は Tsubstitute クラスを定義しています。
このクラスは,ログオンした後 LogonSID をコピーし TAccessPermission クラスを利用してパーミッションを追加したり,削除したりしています。
Unit4 は TAccessPermission クラスを定義しています。
オブジェクトのセキュリティ記述子を取得し,そのセキュリティ識別子の中の随意アクセス制御リストを取得しています。
取得したアクセス制御リストのサイズを求め,新しくアクセス制御リストを作成して,アクセス制御エントリを追加しています。
新しく作成したアクセス制御リストを,新しく作成したセキュリティ記述子に設定しています。アクセス制御エントリの削除も行っています。
※ GetUserObjectSecurity で取得したセキュリティ記述子を利用せずに新しく作成し直しているのは,SetUserObjectSecurity で設定するセキュリティ記述子とGetUserObjectSecurity で取得されるセキュリティ記述子のフォーマットが異なるためです。
ソースコード ダウンロード
3.1 アプリケーションイメージ
3.2 説明
このプログラムを実行すると指定したユーザーでアプリケーションを実行できます。
次のイメージは,このプログラムで CMD.EXE( MS-DOS プロンプト ) を実行したものです。
Guest ユーザーから,Administrator ユーザーへ偽装しています。
3.3 実行イメージ
3.4 whoami のソースコード
この whoami プログラムは Win32 のコンソールアプリケーションです。
Windows NT リソースキットと同じように,ドメイン名とユーザー名を表示します。
ソースコード ダウンロード
3.5 権限の設定
LoginUser と CreateProcessAsUser を実行するプログラムは,上記で説明した通りSE_TCB_NAME,SE_ASSIGNPRIMARYTOKEN_NAME,SE_INCREASE_QUOTA_NAME権限を与えておく必要があります。これらの権限をユーザーマネージャーで与えます。
ところがユーザーマネージャーを使用すると,この DEFINE に相当する日本語名が判りません。
これらの DEFINE が日本語のどれに相当するか調べるには,LookupPrivilegeDisplayName 関数を使用すると便利でしょう。詳細につきましてはオンラインヘルプを参照して下さい。
この権限の説明は WindowsNT のリソースキットでも説明されています。
上記の3つの権限に対応する日本語の記述は次になります。
SE_TCB_NAME
オペレーティングシステムの一部として機能
SE_ASSIGNPRIMARYTOKEN_NAME
プロセスレベルトークンの置き換え
SE_INCREASE_QUOTA_NAME
クォータの増加
※ 実行結果イメージの環境では,Guest ユーザーにこれらの3つの権限を与えています。
3.6 最後に
今回試した例は, Guest ユーザーに Administrator レベルの権限をいくつか与えています。
この例題と同じように Guest ユーザーに
SE_TCB_NAME
SE_ASSIGNPRIMARYTOKEN_NAME
SE_INCREASE_QUOTA_NAME
の3つを与えた場合は,セキュリティの観点から速やかに ユーザーマネージャーを使用して Guest ユーザーから,これらの権限を外された方が良いでしょう。
特にインターネットに接続してある WindowsNT Server で試された方は注意して下さい。