EternalWindows
GINA / ログオン後の処理

WlxLoggedOutSASでユーザーのトークンを取得すれば、 それを基に一連の引数の初期化を行うことができます。 この引数の中にはトークンのハンドルが含まれているため、まずはこれを初期化します。

*phToken = lpgc->hToken;

次に、pAuthenticationIdの初期化について考えていきます。 この引数にはログオンセッションの識別子を格納することになっていますが、 LogonUser(Ex)はこの識別子を返しません。 しかし、トークンの情報を取得するGetTokenInformationは、 TOKEN_STATISTS構造体にログオンセッションの識別子を設定するため、 この性質を利用すればpAuthenticationIdを初期化することができます。

GetTokenInformation(*phToken, TokenStatistics, (PVOID)&tokenStatistics, sizeof(TOKEN_STATISTICS), &dwSize);
*pAuthenticationId = tokenStatistics.AuthenticationId;

TOKEN_STATISTS構造体を取得するには、第2引数にTokenStatisticsを指定します。 ログオンセッションの識別子はAuthenticationIdメンバに格納されるため、 後は単純にこれを*pAuthenticationIdに代入すればよいことになります。

続いて、pLogonSidの初期化について考えていきます。 この引数にはログオンSIDを格納することになっています。 Winlogonは、defaultデスクトップを作成するときに、 このログオンSIDを持つACEをdefaultデスクトップのDACLに設定します。 ログオンSIDはランダムな値で作成されてトークンに含まれるため、 defaultデスクトップへアクセスできるのはこのログオンSIDを持つユーザー、 つまりWlxLoggedOutSASでログオンしたユーザーだけとなり、 defaultデスクトップが保護されることになります。

GetTokenInformation(*phToken, TokenGroups, NULL, 0, &dwSize);
pTokenGroups = (PTOKEN_GROUPS)LocalAlloc(LPTR, dwSize);

GetTokenInformation(*phToken, TokenGroups, pTokenGroups, dwSize, &dwSize);
for (i = 0; i < pTokenGroups->GroupCount; i++) {
	if (pTokenGroups->Groups[i].Attributes & SE_GROUP_LOGON_ID) {
		CopySid(GetLengthSid(pLogonSid), pLogonSid, pTokenGroups->Groups[i].Sid);
		break;
	}
}

LocalFree(pTokenGroups);

ログオンSIDはトークングループに格納されており、そのサイズが一定でないことから、 まず必要なサイズ分のメモリを確保し、そのうえでデータを取得することになります。 各グループSIDはpTokenGroups->Groups[i]で表すことができ、 そのAttributesメンバがSE_GROUP_LOGON_IDである場合は、 pTokenGroups->Groups[i].SidがログオンSIDであることを意味しています。 よって、このときにCopySidでpLogonSidにpTokenGroups->Groups[i].Sidをコピーします。 CopySidの第1引数はコピーの先のサイズですが、ログオンSIDのフォーマットは固定であるため、 pLogonSidのサイズがpTokenGroups->Groups[i].Sidより低いようなことは起こりえません。 なお、ログオンにおいてWindowsXPから登場したLogonUserExを呼び出した場合、 関数成功時に引数を通じてログオンSIDを取得することができるため、 上記のようなトークンループの取得と走査は必要ありません。

最後に、ユーザープロファイルに関する引数について考えていきます。

*pProfile = NULL;
*pdwOptions = 0;

pProfileはWinlogonにロードさせたいユーザープロファイルを指定しますが、 NULLにした場合はWinlogonが適切なプロファイルをロードしてくれることになっています。 pdwOptionsにWLX_LOGON_OPT_NO_PROFILEを指定すると、 Winlogonにプロファイルをロードしないよう指示することができますが、 プロファイルがロードされなければデスクトップのファイルやHKEY_CURRENT_USERが 利用できなくなるため、余程の理由がない限りは避けておいたほうがよいでしょう。

移動ユーザープロファイルのロード

ユーザープロファイルには移動ユーザープロファイルという ネットワーク上のサーバーに存在するプロファイルを指すことがありますが、 このプロファイルをロードしたい場合は、pProfileにNULLを指定すべきではありません。 移動ユーザープロファイルは、ログオンするドメインのドメインコントローラに存在しますから、 まずはドメインから関連するドメインコントローラの名前を取得し、 その後、その名前とユーザー名を基にプロファイルのパスを取得します。 次に、コード例を示します。

LPWSTR            lpszComputerName;
LPUSER_INFO_3     lpUserInfo;
PWLX_PROFILE_V1_0 pWlxProfile;

NetGetDCName(NULL, lpgc->pMprNotifyInfo->pszDomain, (LPBYTE *)&lpszComputerName); // ドメインからドメインコントローラの名前を取得

NetUserGetInfo(lpszComputerName, lpgc->pMprNotifyInfo->pszUserName, 3, (LPBYTE *)&lpUserInfo); // ドメインコントローラ上のユーザー情報を取得
if (lpUserInfo->usri3_profile != L'\0') {
	pWlxProfile             = (PWLX_PROFILE_V1_0)LocalAlloc(LPTR, sizeof(WLX_PROFILE_V1_0));
	pWlxProfile->dwType     = WLX_PROFILE_TYPE_V1_0;
	pWlxProfile->pszProfile = (PWSTR)LocalAlloc(LPTR, (lstrlen(lpUserInfo->usri3_profile) + 1) * sizeof(WCHAR));
	lstrcpy(pWlxProfile->pszProfile, lpUserInfo->usri3_profile);

	*pProfile = (PVOID)pWlxProfile;
}
else // プロファイルが存在しなかった。(つまり、ドメインコントローラはこのユーザーのプロファイルを定義していない)
	*pProfile = NULL;

NetApiBufferFree(lpszComputer);
NetApiBufferFree(lpUserInfo);

NetUserGetInfoはプロファイルのパスが存在する場合は空でない文字列を返すため、 文字列が'\0'で終了するかどうかを調べれば、プロファイルの存在を確認できます。 プロファイルが存在する場合は、*pProfileにWLX_PROFILE_V1_0構造体のアドレスを格納するため、 まずはその分のメモリを確保します。 初期化すべきメンバはdwTypeとpszProfileの2つで、dwTypeは常にWLX_PROFILE_TYPE_V1_0、 pszProfileにはプロファイルのパスを指定します。



戻る