ユーザープロファイル
はじめに
ウィンドウズでは、ユーザー毎に保持しているデータ、システムワイドのデータ、などいろいろなレベルのデータがあります。このため、データ の格納場所をしっかり把握しておくことは重要です。システムワイドのデータだと思っていたものがユーザー別のデータだったりすると、ユーザーが切り替わった だけで途端に使えなかったりします。典型的な例はプリンタの情報だ。プリンタの情報はユーザープロファイルに保存される。このため、ある特定のユーザープロ ファイルがロードされているときには、あるプリンタの情報が使えたのに、ある時には(そのユーザープロファイルがロードされていなかったために)使えな かったりします。ここでは、ユーザープロファイルをプログラムから明示的にロードする方法を紹介します。
ユーザープロファイルのロード
システムにローカルログオン(パソコンの前に座って普通に使う場合)する場合には、システムへのログオンと同時にユーザープロファイル はロードされる。しかし、ネットワーク経由でユーザーがコンピュータにアクセスし、サーバープログラムでそのユーザーを偽装するような場合には、ユーザー プロファイルがロードされるとは限らない。
具体的に API レベルで言うとこういうことです。
ネットワーク経由であるユーザーがサーバーに接続し、サーバーの機能を使う場合、そのユーザーの権限は、典型的には次のような流れにな る。
- サーバーがある特定のユーザーアカウントで実行されている
(LocalSystem, NetworkService など) - TCP/IP でユーザーが接続する
- サーバーの受け側スレッドでは、ユーザーのクレデンシャル(ユーザー名とパスワードを基にした情報)を要求する
- クライアントはサーバーに対して、要求されたクレデンシャルを送信する
- サーバーではクレデンシャルを基に、トークンを生成する.
このとき LogonUser などでシステムにログオンし、その結果トークンを得る。 - サーバーではそのトークンを使って、サーバーの要求を処理するスレッドに偽装トークンを設定する
この際には、ImpersonateLoggedOnUser などを用いる - クライアントの要求を処理する
- サーバー側で処理が終わったら RevertToSelf などで元のトークンに戻る
こうして、サーバーはクライアントからの要求を処理する。
もっとも、クライアントの要求を処理するだけなら、基本的にクライアントを偽装する 「必要性」 は無いのだけれど、Windows は基本的にユーザープリンシパルを基にしたセキュリティシステムであるため、クライアントを偽装した方がクライアントのアクセス範囲に一貫性が保たれるの で都合が良いのだ。例えば、サーバーのスレッドを常に LocalSystem で実行させてしまうと、そのユーザーはローカルログオンしたときは、そのシステムに対して大した権限を持たないとしても、ネットワーク経由でアクセスする と LocalSystem 相当の権限を手に入れてしまう、という不整合が発生する。このため、サーバーを設計するときには通常はクライアントを偽装するのが基本なのだ。
少し脱線してしまった。プロファイルに話を戻す。
さて、上記の 1 ~ 8 の流れを見てもらえばわかるように、特にプロファイルをロードする処理は行わないのが普通だ。何しろ 「重い」 から。LogonUser を呼び出したとき自動的には、プロファイルはロードされないことに注意されたい。
プロファイルをロードするためには、その名もズバリ、LoadUserProfile という API を呼び出せばよい。
コード例
以上、くどくど書いてきたが結局のところプロファイルをロードするには以下のように LoadUserProfile API を使えばよい。ここではコードの簡便化のため g_hToken と g_hProfile はグローバルに持たせてあるが適当に直していただきたい。要は LoonUser でトークンを得て、LoadUserProfile でプロファイル(ハンドル)を取得できれば良いのだ。
HANDLE g_hToken;
HANDLE g_hProfile;
...
BOOL LoadProfile (LPTSTR pszDomain, LPTSTR pszUser, LPTSTR pszPassword,
INT nLogonType, INT nProvider) {
BOOL bRet = TRUE;
if (!LogonUser(pszUser,
pszDomain, pszPassword, nLogonType, nProvider, &g_hToken) ) {
return
FALSE;
}
PROFILEINFO pi;
::ZeroMemory(&pi,
sizeof(PROFILEINFO));
pi.dwSize = sizeof(PROFILEINFO);
pi.lpUserName = pszUser;
if (!LoadUserProfile(g_hToken,
&pi)) {
CloseHandle (g_hToken);
g_hToken
= NULL;
return
FALSE;
}
g_hProfile = pi.hProfile;
return bRet;
}