EternalWindows
アカウント管理 / ユーザーの作成

自分が使用しているPCを他の人にも使用させる場合、 その人のためのユーザーを作成することはよくあります。 これにより、その人はその作成したユーザーでログオンするため、 作成したファイルが他の人に干渉される心配はなくなります。 新しいユーザーを作成するには、NetUserAddを呼び出します。

NET_API_STATUS NetUserAdd(
  LMSTR servername,
  DWORD level,
  LPBYTE buf,
  LPDWORD parm_err
);

servernameは、servernameは、この関数を実行するサーバー名を指定します。 NULLを指定した場合は、ローカルコンピュータで実行されます。 levelは、情報レベルを指定します。 bufは、ユーザー情報を格納したバッファを指定します。 parm_errは、エラーの原因となったメンバのインデックスを受け取る変数のアドレスを指定します。 どのメンバに問題があるのかを特定したくない場合は、NULLを指定しても問題ありません。

NetUserAddで作成したユーザーを削除するには、NetUserDelを呼び出します。

NET_API_STATUS NetUserDel(
  LPCWSTR servername,
  LPCWSTR username
);

servernameは、servernameは、この関数を実行するサーバー名を指定します。 NULLを指定した場合は、ローカルコンピュータで実行されます。 usernameは、削除したいユーザーの名前を指定します。

ユーザーを作成した段階では、そのユーザーはどのようなグループにも属していません。 既存のユーザーを特定のグループに追加するには、NetLocalGroupAddMembersを呼び出します。

NET_API_STATUS NetLocalGroupAddMembers(
  LPCWSTR servername,
  LPCWSTR groupname,
  DWORD level,
  LPBYTE buf,
  DWORD totalentries
);

servernameは、servernameは、この関数を実行するサーバー名を指定します。 NULLを指定した場合は、ローカルコンピュータで実行されます。 groupnameは、ユーザーの追加先となるグループ名を指定します。 levelは、情報レベルを指定します。 bufは、追加するユーザーを格納したバッファを指定します。 totalentriesは、bufに格納したユーザーの数を指定します。

今回のプログラムは、NetUserAddでユーザーを作成します。 管理者として実行しなければ失敗することになります。

#include <windows.h>
#include <lm.h>
#include <userenv.h>
#include <sddl.h>

#pragma comment (lib, "netapi32.lib")
#pragma comment (lib, "userenv.lib")

BOOL ArrangeProfile(LPWSTR lpszUserName, BOOL bCreate);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	BOOL  bAdd = TRUE;
	BOOL  bConsoleLogon = TRUE;
	WCHAR szUserName[] = L"myuser";

	if (bAdd) {
		DWORD       dwPrivilege = USER_PRIV_USER;
		DWORD       dwError;
		USER_INFO_1 userInfo;
		
		userInfo.usri1_name        = szUserName;
		userInfo.usri1_password    = L"pass";
		userInfo.usri1_priv        = dwPrivilege;
		userInfo.usri1_home_dir    = NULL;
		userInfo.usri1_comment     = NULL;
		userInfo.usri1_flags       = UF_SCRIPT | UF_DONT_EXPIRE_PASSWD;
		userInfo.usri1_script_path = NULL;

		if (NetUserAdd(NULL, 1, (LPBYTE)&userInfo, &dwError) != NERR_Success) {
			MessageBox(NULL, TEXT("ユーザーの作成に失敗しました。"), NULL, MB_ICONWARNING);
			return 0;
		}
		
		MessageBox(NULL, TEXT("ユーザーを作成しました。"), TEXT("OK"), MB_OK);

		if (bConsoleLogon) {
			WCHAR                     szDomainAndName[1024];
			WCHAR                     szDomainName[256];
			WCHAR                     szGroupName[256];
			DWORD                     dwSize;
			LOCALGROUP_MEMBERS_INFO_3 membersInfo;

			if (dwPrivilege == USER_PRIV_USER)
				lstrcpyW(szGroupName, L"Users");
			else if (dwPrivilege == USER_PRIV_ADMIN)
				lstrcpyW(szGroupName, L"Adminstrators");
			else if (dwPrivilege == USER_PRIV_GUEST)
				lstrcpyW(szGroupName, L"Guests");
			else
				;

			dwSize = sizeof(szDomainName) / sizeof(TCHAR);
			GetComputerName(szDomainName, &dwSize);
			wsprintfW(szDomainAndName, L"%s\\%s", szDomainName, szUserName);

			membersInfo.lgrmi3_domainandname = szDomainAndName;
			if (NetLocalGroupAddMembers(NULL, szGroupName, 3, (LPBYTE)&membersInfo, 1) != NERR_Success) {
				MessageBox(NULL, TEXT("ユーザーをグループへ追加することに失敗しました。"), NULL, MB_ICONWARNING);
				return 0;
			}

			if (!ArrangeProfile(szUserName, TRUE)) {
				MessageBox(NULL, TEXT("ユーザープロファイルの作成に失敗しました。"), NULL, MB_ICONWARNING);
				return 0;
			}
		}
	}
	else {
		if (NetUserDel(NULL, szUserName)  != NERR_Success) {
			MessageBox(NULL, TEXT("ユーザーの削除に失敗しました。"), NULL, MB_ICONWARNING);
			return 0;
		}

		if (bConsoleLogon) {
			if (!ArrangeProfile(szUserName, FALSE)) {
				MessageBox(NULL, TEXT("ユーザープロファイルの削除に失敗しました。"), NULL, MB_ICONWARNING);
				return 0;
			}
		}

		MessageBox(NULL, TEXT("ユーザーを削除しました。"), TEXT("OK"), MB_OK);
	}

	return 0;
}

BOOL ArrangeProfile(LPWSTR lpszUserName, BOOL bCreate)
{
	PUSER_INFO_4 pUserInfo;
	LPWSTR       lpszSid;
	BOOL         bResult;
	HRESULT      hr;
	
	if (NetUserGetInfo(NULL, lpszUserName, 4, (LPBYTE *)&pUserInfo) != NERR_Success)
		return FALSE;
	
	ConvertSidToStringSidW(pUserInfo->usri4_user_sid, &lpszSid);

	if (bCreate) {
		WCHAR szPath[MAX_PATH];
		hr = CreateProfile(lpszSid, lpszUserName, szPath, MAX_PATH);
		bResult = hr == S_OK;
	}
	else
		bResult = DeleteProfile(lpszSid, NULL, NULL);

	NetApiBufferFree(pUserInfo);

	return bResult; 
}

bAddがTRUEである場合は、NetUserAddを呼び出してユーザーを作成し、 FALSEの場合はNetUserDelでユーザーを削除します。 NetUserAddを呼び出しは、次のようになっています。

userInfo.usri1_name        = szUserName;
userInfo.usri1_password    = L"pass";
userInfo.usri1_priv        = dwPrivilege;
userInfo.usri1_home_dir    = NULL;
userInfo.usri1_comment     = NULL;
userInfo.usri1_flags       = UF_SCRIPT | UF_DONT_EXPIRE_PASSWD;
userInfo.usri1_script_path = NULL;

if (NetUserAdd(NULL, 1, (LPBYTE)&userInfo, &dwError) != NERR_Success) {
	MessageBox(NULL, TEXT("ユーザーの作成に失敗しました。"), NULL, MB_ICONWARNING);
	return 0;
}

情報レベルに1を指定しているため、USER_INFO_1構造体を使用することになります。 usri1_nameに作成したいユーザー名を指定し、 usri1_passwordにそのユーザーのパスワードを指定します。 NULLを指定した場合は、パスワードを指定せずにログオンできるようになります。 usri1_privは、USER_PRIV_USERを指定しているため、 このユーザーは標準ユーザーになります。 usri1_home_dirやusri1_comment、usri1_script_pathはNULLを指定しても問題ありません。 usri1_flagsに指定しているUF_SCRIPTは、ログオン時にスクリプトファイルを実行することを意味し、 UF_DONT_EXPIRE_PASSWDはパスワードが期限切れでないことを意味します。

bConsoleLogonがTRUEである場合は、作成したユーザーをUsersなどの既定のグループに属させようとしています。 これは、このようなグループに属さなければ、コンソールへのログオンができないためです。 より具体的に言えば、Windowsのログオン画面やコントロールパネルのユーザーアカウントおいて、 作成したユーザーのアイコンが表示されないことになります。 グループへユーザーを追加する処理は次のようになっています。

if (dwPrivilege == USER_PRIV_USER)
	lstrcpyW(szGroupName, L"Users");
else if (dwPrivilege == USER_PRIV_ADMIN)
	lstrcpyW(szGroupName, L"Adminstrators");
else if (dwPrivilege == USER_PRIV_GUEST)
	lstrcpyW(szGroupName, L"Guests");
else
	;

dwSize = sizeof(szDomainName) / sizeof(TCHAR);
GetComputerName(szDomainName, &dwSize);
wsprintfW(szDomainAndName, L"%s\\%s", szDomainName, szUserName);

membersInfo.lgrmi3_domainandname = szDomainAndName;
if (NetLocalGroupAddMembers(NULL, szGroupName, 3, (LPBYTE)&membersInfo, 1) != NERR_Success) {
	MessageBox(NULL, TEXT("ユーザーをグループへ追加することに失敗しました。"), NULL, MB_ICONWARNING);
	return 0;
}

まず、特権レベルに合してグループ名を決定します。 USER_PRIV_USERの場合に、グループ名をAdminstratorsにしたりするのは、 特権レベルと矛盾が発生するため好ましくありませんが、 このような場合は特権レベルがUSER_PRIV_ADMINに昇格するようです。 グループへ追加したいユーザーを格納するのは、 LOCALGROUP_MEMBERS_INFO_3構造体ですが、 この構造体はユーザー名を「ドメイン名\\ユーザー名」の形で要求するため、 GetComputerNameでコンピュータ名を取得し、 これとユーザー名を\\で連結するようにします。 NetLocalGroupAddMembersでは、第2引数に追加先となるグループ名を指定し、 第4引数に追加するユーザー名を格納した構造体を指定します。

明示的に既定のグループに属さなくても、 トークン上ではそのようなグループに属していることを補足しておきます。 たとえば、USER_PRIV_USERを指定してユーザーを作成し、 そのユーザーをLogonUserでログオンさせてトークンのトークングループを走査した場合、 そこにはUsersのSIDが含まれていることが分かります。 つまり、トークンを偽装してそのユーザーとしてコードを実行した場合、 そのユーザーはUsersに属しているものと解釈されることになります。

ArrangeProfileは、ユーザープロファイルの作成または削除を行います。 ユーザープロファイルとは簡単にいえば、C:\Users以下に存在するフォルダであり、 今回の場合であればC:\Users\myuserというフォルダが作成されます。 ユーザープロファイルが存在しなければコンソールへのログオンは失敗するため、これは必須です。

BOOL ArrangeProfile(LPWSTR lpszUserName, BOOL bCreate)
{
	PUSER_INFO_4 pUserInfo;
	LPWSTR       lpszSid;
	BOOL         bResult;
	HRESULT      hr;
	
	if (NetUserGetInfo(NULL, lpszUserName, 4, (LPBYTE *)&pUserInfo) != NERR_Success)
		return FALSE;
	
	ConvertSidToStringSidW(pUserInfo->usri4_user_sid, &lpszSid);

	if (bCreate) {
		WCHAR szPath[MAX_PATH];
		hr = CreateProfile(lpszSid, lpszUserName, szPath, MAX_PATH);
		bResult = hr == S_OK;
	}
	else
		bResult = DeleteProfile(lpszSid, NULL, NULL);

	NetApiBufferFree(pUserInfo);

	return bResult; 
}

ユーザープロファイルを作成するCreateProfileは、第1引数にユーザーのSIDを文字列として要求します。 ユーザーのSIDはNetUserGetInfoで取得できるため、これをConvertSidToStringSidで文字列に変換すればよいでしょう。 ユーザープロファイルの作成に成功した場合は、HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\以下が更新されます。


戻る