啟用服務帳戶以存取 SCP 屬性
下列程式代碼範例會在服務連接點 (SCP) 對象上設定一對 存取控制 Entries (ACE)。 ACE 會將讀取/寫入許可權授與服務實例執行所在的使用者或計算機帳戶。 服務安裝程式會使用類似下列的程式代碼,以確保服務可以在運行時間更新其屬性。 如果未設定類似這些的 ACE,服務將無法存取 SCP 的屬性。
一般而言,服務安裝程式會在建立SCP對象之後設定這些ACE。 如需詳細資訊,以及建立SCP並呼叫此函式的程式碼範例,請參閱用戶端如何尋找和使用服務 連線點。 如果服務重新設定為在不同的帳戶下執行,則必須更新 ACE。 若要成功執行,此程式碼範例必須在網域系統管理員的安全性內容中執行。
範例函式的第一個參數會指定要授與存取權的用戶帳戶名稱。 函式假設名稱為 Domain**\**UserName 格式。 如果未指定任何帳戶,函式會假設服務會使用LocalSystem帳戶。 這表示函式必須授與服務執行所在的主機伺服器電腦帳戶的存取權。 若要這樣做,程式代碼範例會呼叫 GetComputerObjectName 函式,以取得本機計算機的網域和用戶名稱。
下列程式代碼範例可以修改為授與服務對 SCP 物件的完整存取權,但最佳做法是只授與服務在運行時間所需的特定訪問許可權。 在此情況下,函式會授與兩個屬性的存取權。
屬性 | 說明 |
---|---|
serviceDNSName | 服務執行所在的主機伺服器名稱。 |
serviceBindingInformation | 服務啟動時所更新的私人系結資訊。 |
每個屬性都是由屬性之 attributeSchema 類別的schemaIDGUID來識別。 架構中的每個屬性都有自己的唯 一 schemaIDGUID。 下列程式代碼範例會使用字串來指定 GUID。 GUID 字串具有下列格式,其中每個 「X」 都會以十六進位數位取代:{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXXX}。
如需指派給屬性以授與或拒絕存取的schemaIDGUID值,請參閱Active Directory 架構參考頁面。
下列程式代碼範例使用 IADsSecurityDescriptor、IADsAccessControlList 和 IADsAccessControlEntry 介面來執行下列作業。
- 取得 SCP 對象的安全性描述元。
- 在安全性描述元的選擇性訪問控制清單 (DACL) 中設定適當的 ACE。
- 修改 SCP 對象的安全性描述元。
#include <atlbase.h>
//******************************
//
// AllowAccessToScpProperties()
//
//******************************
HRESULT AllowAccessToScpProperties(
LPWSTR wszAccountSAM, // Service account to allow access.
IADs *pSCPObject) // IADs pointer to the SCP object.
{
HRESULT hr = E_FAIL;
IADsAccessControlList *pACL = NULL;
IADsSecurityDescriptor *pSD = NULL;
IDispatch *pDisp = NULL;
IADsAccessControlEntry *pACE1 = NULL;
IADsAccessControlEntry *pACE2 = NULL;
IDispatch *pDispACE = NULL;
long lFlags = 0L;
CComBSTR sbstrTrustee;
CComBSTR sbstrSecurityDescriptor = L"nTSecurityDescriptor";
VARIANT varSD;
if(NULL == pSCPObject)
{
return E_INVALIDARG;
}
VariantInit(&varSD);
/*
If no service account is specified, service runs under
LocalSystem. Allow access to the computer account of the
service's host.
*/
if (wszAccountSAM)
{
sbstrTrustee = wszAccountSAM;
}
else
{
LPWSTR pwszComputerName;
DWORD dwLen;
// Get the size required for the SAM account name.
dwLen = 0;
GetComputerObjectNameW(NameSamCompatible,
NULL, &dwLen);
pwszComputerName = new WCHAR[dwLen + 1];
if(NULL == pwszComputerName)
{
hr = E_OUTOFMEMORY;
goto cleanup;
}
/*
Get the SAM account name of the computer object for
the server.
*/
if(!GetComputerObjectNameW(NameSamCompatible,
pwszComputerName, &dwLen))
{
delete pwszComputerName;
hr = HRESULT_FROM_WIN32(GetLastError());
goto cleanup;
}
sbstrTrustee = pwszComputerName;
wprintf(L"GetComputerObjectName: %s\n", pwszComputerName);
delete pwszComputerName;
}
// Get the nTSecurityDescriptor.
hr = pSCPObject->Get(sbstrSecurityDescriptor, &varSD);
if (FAILED(hr) || (varSD.vt != VT_DISPATCH))
{
_tprintf(TEXT("Get nTSecurityDescriptor failed: 0x%x\n"), hr);
goto cleanup;
}
/*
Use the V_DISPATCH macro to get the IDispatch pointer from
VARIANT structure and QueryInterface for an
IADsSecurityDescriptor pointer.
*/
hr = V_DISPATCH( &varSD )->QueryInterface(
IID_IADsSecurityDescriptor,
(void**)&pSD);
if (FAILED(hr))
{
_tprintf(
TEXT("Cannot get IADsSecurityDescriptor: 0x%x\n"),
hr);
goto cleanup;
}
/*
Get an IADsAccessControlList pointer to the security
descriptor's DACL.
*/
hr = pSD->get_DiscretionaryAcl(&pDisp);
if (SUCCEEDED(hr))
{
hr = pDisp->QueryInterface(
IID_IADsAccessControlList,
(void**)&pACL);
}
if (FAILED(hr))
{
_tprintf(TEXT("Cannot get DACL: 0x%x\n"), hr);
goto cleanup;
}
// Create the COM object for the first ACE.
hr = CoCreateInstance(CLSID_AccessControlEntry,
NULL,
CLSCTX_INPROC_SERVER,
IID_IADsAccessControlEntry,
(void **)&pACE1);
// Create the COM object for the second ACE.
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(CLSID_AccessControlEntry,
NULL,
CLSCTX_INPROC_SERVER,
IID_IADsAccessControlEntry,
(void **)&pACE2);
}
if (FAILED(hr))
{
_tprintf(TEXT("Cannot create ACEs: 0x%x\n"), hr);
goto cleanup;
}
// Set the properties of the two ACEs.
// Allow read and write access to the property.
hr = pACE1->put_AccessMask(
ADS_RIGHT_DS_READ_PROP | ADS_RIGHT_DS_WRITE_PROP);
hr = pACE2->put_AccessMask(
ADS_RIGHT_DS_READ_PROP | ADS_RIGHT_DS_WRITE_PROP);
// Set the trustee, which is either the service account or the
// host computer account.
hr = pACE1->put_Trustee( sbstrTrustee );
hr = pACE2->put_Trustee( sbstrTrustee );
// Set the ACE type.
hr = pACE1->put_AceType( ADS_ACETYPE_ACCESS_ALLOWED_OBJECT );
hr = pACE2->put_AceType( ADS_ACETYPE_ACCESS_ALLOWED_OBJECT );
// Set AceFlags to zero because ACE is not inheritable.
hr = pACE1->put_AceFlags( 0 );
hr = pACE2->put_AceFlags( 0 );
// Set Flags to indicate an ACE that protects a specified object.
hr = pACE1->put_Flags( ADS_FLAG_OBJECT_TYPE_PRESENT );
hr = pACE2->put_Flags( ADS_FLAG_OBJECT_TYPE_PRESENT );
// Set ObjectType to the schemaIDGUID of the attribute.
// serviceDNSName
hr = pACE1->put_ObjectType(
L"{28630eb8-41d5-11d1-a9c1-0000f80367c1}");
// serviceBindingInformation
hr = pACE2->put_ObjectType(
L"{b7b1311c-b82e-11d0-afee-0000f80367c1}");
/*
Add the ACEs to the DACL. Need an IDispatch pointer for
each ACE to pass to the AddAce method.
*/
hr = pACE1->QueryInterface(IID_IDispatch,(void**)&pDispACE);
if (SUCCEEDED(hr))
{
hr = pACL->AddAce(pDispACE);
}
if (FAILED(hr))
{
_tprintf(TEXT("Cannot add first ACE: 0x%x\n"), hr);
goto cleanup;
}
else
{
if (pDispACE)
pDispACE->Release();
pDispACE = NULL;
}
// Repeat for the second ACE.
hr = pACE2->QueryInterface(IID_IDispatch, (void**)&pDispACE);
if (SUCCEEDED(hr))
{
hr = pACL->AddAce(pDispACE);
}
if (FAILED(hr))
{
_tprintf(TEXT("Cannot add second ACE: 0x%x\n"), hr);
goto cleanup;
}
// Write the modified DACL back to the security descriptor.
hr = pSD->put_DiscretionaryAcl(pDisp);
if (SUCCEEDED(hr))
{
/*
Write the ntSecurityDescriptor property to the
property cache.
*/
hr = pSCPObject->Put(sbstrSecurityDescriptor, varSD);
if (SUCCEEDED(hr))
{
// SetInfo updates the SCP object in the directory.
hr = pSCPObject->SetInfo();
}
}
cleanup:
if (pDispACE)
pDispACE->Release();
if (pACE1)
pACE1->Release();
if (pACE2)
pACE2->Release();
if (pACL)
pACL->Release();
if (pDisp)
pDisp->Release();
if (pSD)
pSD->Release();
VariantClear(&varSD);
return hr;
}