ユーザーに「サービスとしてログオン」権限を付与する
特定のサービスをユーザーアカウントで起動しようとすると、ユーザーにサービス権限がないことが問題になることがある。特にVistaでは、Administratorsに所属していてもこの権限がないので、いろいろとやっかいだ。
これを解決するために、
// espresso3389というユーザーにサービス起動の権限を付与する addSeServiceLogonRightToUser("espresso3389");
という感じで使える関数を作成してみた。
#include <windows.h> #include <sddl.h> #include <iads.h> #include <ntsecapi.h> #include <vector> #include "atlbase.h" #include "atlstr.h" using namespace ATL; // アカウント名からSIDと所属ドメイン名を取得する PSID lookupAccountName(LPCTSTR pszName, CString& domainName, std::vector<BYTE>& buf) { DWORD domNameSize = 0; DWORD dwSidSize = 0; SID_NAME_USE snu; LookupAccountName(NULL, pszName, NULL, &dwSidSize, NULL, &domNameSize, &snu); if(dwSidSize && domNameSize) { buf.resize(dwSidSize); PSID psid = (PSID)&buf[0]; LookupAccountName(NULL, pszName, psid, &dwSidSize, domainName.GetBufferSetLength(domNameSize), &domNameSize, &snu); return psid; } return NULL; } // 指定のユーザーに指定の権限を付与する bool addAccountRight(LPCTSTR pszName, LPCTSTR right) { std::vector<BYTE> buf; CString domainName; PSID psid = lookupAccountName(pszName, domainName, buf); if(!psid) { return false; } CStringW rightW = CT2W(right); LSA_HANDLE handle; LSA_OBJECT_ATTRIBUTES objAttr; ZeroMemory(&objAttr, sizeof(objAttr)); NTSTATUS status = LsaOpenPolicy( NULL, &objAttr, POLICY_ALL_ACCESS, &handle); if(status) { SetLastError(LsaNtStatusToWinError(status)); return false; } USHORT size = (USHORT)rightW.GetLength() * 2; LSA_UNICODE_STRING rights = {size, size, (LPWSTR)(LPCWSTR)rightW}; status = LsaAddAccountRights(handle, psid, &rights, 1); if(status) { DWORD err = LsaNtStatusToWinError(status); LsaClose(handle); SetLastError(err); return false; } LsaClose(handle); return true; } // 指定のユーザーにSeServiceLogonRightを付与する bool addSeServiceLogonRightToUser(LPCTSTR pszName) { return addAccountRight(pszName, _T("SeServiceLogonRight")); }
後に気づいたが、同様のコードが、KB132958: How To Manage User Privileges Programmatically in Windows NTにある。