EternalWindows
Credentials Management / ログオンセッション単位の保存

クレデンシャルの保存期間は、CREDENTIAL構造体のPersistの値によって決定することになっています。 この値がCRED_PERSIST_ENTERPRISEである場合は、クレデンシャルは永続的に保存され、 システムを再起動しても依然として利用可能です。 一方、CRED_PERSIST_SESSIONを指定した場合は、クレデンシャルが現在のユーザーのログオンセッションに関連付けられることになり、 ユーザーがログオフした場合はクレデンシャルが削除されます。 クレデンシャルが永続的に保存されることを望まない場合は、このCRED_PERSIST_SESSIONを指定することになります。

CredUIPromptForCredentialsやCredUIConfirmCredentialsでクレデンシャルを保存した場合は、 Persistの値がCRED_PERSIST_ENTERPRISE固定になります。 しかし、CREDENTIAL構造体を受け取るCredWriteを呼び出せば、クレデンシャルの情報を自由に決定することができます。

BOOL CredWrite(
  PCREDENTIAL Credential,
  DWORD Flags
);

Credentialは、CREDENTIAL構造体のアドレスを指定します。 Flagsは、通常は0で問題ありません。

ターゲット名からクレデンシャルを取得するには、CredReadを呼び出します。

BOOL CredRead(
  LPCTSTR TargetName,
  DWORD Type,
  DWORD Flags,
  PCREDENTIAL *Credential
);

TargetNameは、取得するクレデンシャルのターゲット名を指定します。 Typeは、CRED_TYPE_GENERICなどのタイプを指定します。 Flagsは、予約されているため0を指定します。 Credentialは、クレデンシャルを受け取る変数のアドレスを指定します。

今回のプログラムは、CredUIPromptForWindowsCredentialsでダイアログを表示し、 チェックボックスにチェックが付けられた場合は、CredWriteでクレデンシャルを保存します。 既にクレデンシャルが存在する場合は、それをダイアログの既定値として表示します。

#include <windows.h>
#include <wincred.h>

#pragma comment (lib, "credui.lib")

BOOL ControlCredential(LPTSTR lpszTargetName, LPTSTR lpszUserName, LPTSTR lpszPassword, BOOL bRead);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR       szTargetName[] = TEXT("cred-target");
	TCHAR       szUserName[256] = {0};
	TCHAR       szDomainName[256] = {0};
	TCHAR       szPassword[256] = {0};
	DWORD       dwUserNameSize = sizeof(szUserName) / sizeof(TCHAR);
	DWORD       dwDomainSize = sizeof(szDomainName) / sizeof(TCHAR);
	DWORD       dwPasswordSize = sizeof(szPassword) / sizeof(TCHAR);
	DWORD       dwResult;
	DWORD       dwOutBufferSize;
	DWORD       dwInBufferSize;
	PVOID       pOutBuffer;
	PVOID       pInBuffer;
	ULONG       uAuthPackage = 0;
	BOOL        bSave = TRUE;
	CREDUI_INFO uiInfo;
	HANDLE      hToken;

	uiInfo.cbSize         = sizeof(CREDUI_INFO);
	uiInfo.hwndParent     = NULL;
	uiInfo.pszMessageText = TEXT("message");
	uiInfo.pszCaptionText = TEXT("caption");
	uiInfo.hbmBanner      = NULL;
	
	dwOutBufferSize = 1024;
	pOutBuffer = (PVOID)LocalAlloc(LPTR, dwOutBufferSize);
	
	ControlCredential(szTargetName, szUserName, szPassword, TRUE);
	dwInBufferSize = 1024;
	pInBuffer = (PVOID)LocalAlloc(LPTR, dwInBufferSize);
	CredPackAuthenticationBuffer(0, szUserName, szPassword, (PBYTE)pInBuffer, &dwInBufferSize);

	dwResult = CredUIPromptForWindowsCredentials(&uiInfo, 0, &uAuthPackage, pInBuffer, dwInBufferSize, &pOutBuffer, &dwOutBufferSize, &bSave, CREDUIWIN_GENERIC | CREDUIWIN_CHECKBOX);
	if (dwResult != ERROR_SUCCESS) {
		LocalFree(pOutBuffer);
		return 0;
	}
	
	CredUnPackAuthenticationBuffer(0, pOutBuffer, dwOutBufferSize, szUserName, &dwUserNameSize, szDomainName, &dwDomainSize, szPassword, &dwPasswordSize);
	
	szUserName[dwUserNameSize] = '\0';
	szPassword[dwPasswordSize] = '\0';
	MessageBox(NULL, szUserName, szPassword, MB_OK);

	if (LogonUser(szUserName, NULL, szPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &hToken)) {
		if (bSave)
			ControlCredential(szTargetName, szUserName, szPassword, FALSE);
		CloseHandle(hToken);
	}
	else
		MessageBox(NULL, TEXT("ログオンに失敗しました。"), NULL, MB_ICONWARNING);
	
	SecureZeroMemory(szPassword, sizeof(szPassword));
	SecureZeroMemory(pOutBuffer, dwOutBufferSize);
	LocalFree(pOutBuffer);

	return 0;
}

BOOL ControlCredential(LPTSTR lpszTargetName, LPTSTR lpszUserName, LPTSTR lpszPassword, BOOL bRead)
{
	BOOL        bResult;
	CREDENTIAL  credential;
	PCREDENTIAL pCredential;

	if (bRead) {
		bResult = CredRead(lpszTargetName, CRED_TYPE_GENERIC, 0, &pCredential);
		if (bResult) {
			lstrcpy(lpszUserName, pCredential->UserName);
			CopyMemory(lpszPassword, pCredential->CredentialBlob, pCredential->CredentialBlobSize);
			lpszPassword[pCredential->CredentialBlobSize / sizeof(TCHAR)] = '\0';
			
			CredFree(pCredential);
		}
	}
	else {
		credential.Flags              = 0;
		credential.Type               = CRED_TYPE_GENERIC;
		credential.TargetName         = lpszTargetName;
		credential.Comment            = NULL;
		credential.CredentialBlobSize = lstrlen(lpszPassword) * sizeof(TCHAR);
		credential.CredentialBlob     = (LPBYTE)lpszPassword;
		credential.Persist            = CRED_PERSIST_SESSION;
		credential.AttributeCount     = 0;
		credential.Attributes         = NULL;
		credential.TargetAlias        = NULL;
		credential.UserName           = lpszUserName;

		bResult = CredWrite(&credential, 0);
	}

	return bResult;
}

ControlCredentialという自作関数の第4引数にTRUEを指定した場合、 第1引数のターゲット名で識別されるクレデンシャルが取得されます。 そして、そのクレデンシャルのユーザー名とパスワードが第3引数と第4引数に返ります。 呼び出し側はこれらを基にCredPackAuthenticationBufferを呼び出し、 pInBufferにダイアログの既定値が格納されるようにします。 ただし、CredUIPromptForWindowsCredentialsでは、 パスワードが既定で表示されることはないようです。

CredUIPromptForWindowsCredentialsの第9引数にCREDUIWIN_CHECKBOXを指定した場合は、 ダイアログにチェックボックスが表示されるようになり、 チェックが付けられたかどうかは第8引数から確認することができます。 この関数は、チェックが付けられている場合でも自動でクレデンシャルを保存することはありませんから、 第8引数にTRUEが格納されている場合にアプリケーションが保存処理を行うことになります。 ただし、間違ったクレデンシャルを保存するわけにはいきませんから、 LogonUserが成功した場合のみとします。 ControlCredentialの第4引数にFALSEを指定した場合は、 第1引数のターゲット名を持ったクレデンシャルが作成され、 第2引数のユーザー名と第3引数のパスワードがそれに含まれます。 クレデンシャルのPersistがCRED_PERSIST_SESSIONになっていることから、 クレデンシャルの保存は現在のユーザーがログオフするまで有効です。


戻る