ユーザーに「サービスとしてログオン」権限を付与する

特定のサービスをユーザーアカウントで起動しようとすると、ユーザーにサービス権限がないことが問題になることがある。特に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にある。