Habilitación de la cuenta de servicio para acceder a las propiedades del SCP
En el ejemplo de código siguiente se establece un par de entradas de control de acceso (ACE) en un objeto de punto de conexión de servicio (SCP). Las entradas de control de acceso (ACE) conceden acceso de lectura y escritura a la cuenta de usuario o equipo en la que se ejecutará la instancia de servicio. El instalador del servicio usa código similar al siguiente para asegurarse de que el servicio pueda actualizar sus propiedades en tiempo de ejecución. Si no se establecen entradas ACE similares a estas, el servicio no tendrá acceso a las propiedades del SCP.
Normalmente, el instalador de un servicio establece estas ACE después de crear el objeto SCP. Para obtener más información y un ejemplo de código que crea un SCP y llama a esta función, vea Cómo buscan y usan un punto de conexión de servicio los clientes. Si el servicio se vuelve a configurar para que se ejecute en una cuenta diferente, deben actualizarse las entradas ACE. Para que se ejecute correctamente, este ejemplo de código debe ejecutarse en el contexto de seguridad de un administrador de dominio.
El primer parámetro de la función de ejemplo especifica el nombre de la cuenta de usuario a la que se va a conceder acceso. La función supone que el nombre tiene el formato Dominio**\**Nombre de usuario. Si no se especifica ninguna cuenta, la función supone que el servicio usa la cuenta LocalSystem. Esto significa que la función debe conceder acceso a la cuenta de equipo del servidor host en el que se ejecuta el servicio. Para ello, el ejemplo de código llama a la función GetComputerObjectName para obtener el dominio y el nombre de usuario del equipo local.
El siguiente ejemplo de código se puede modificar para conceder al servicio acceso total al objeto SCP, pero el procedimiento recomendado es conceder solo los derechos de acceso específicos que el servicio requiere en tiempo de ejecución. En este caso, la función concede acceso a dos propiedades.
Propiedad | Descripción |
---|---|
serviceDNSName | Nombre del servidor host en el que se ejecuta el servicio. |
serviceBindingInformation | Información de enlace privado que el servicio actualiza cuando se inicia. |
Cada propiedad se identifica con el schemaIDGUID de la clase attributeSchema de la propiedad. Cada propiedad del esquema tiene su propio schemaIDGUID único. En el siguiente ejemplo de código se usan cadenas para especificar los GUID. Las cadenas GUID tienen el siguiente formato donde cada "X" se reemplaza por un dígito hexadecimal: {XXXXXXXX-XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}.
Consulte las páginas de referencia del esquema de Active Directory para ver los valores de schemaIDGUID asignados a las propiedades para conceder o denegar el acceso a ellas.
En el siguiente ejemplo de código se usan las interfaces IADsSecurityDescriptor, IADsAccessControlList e IADsAccessControlEntry para realizar las siguientes operaciones:
- Obtener el descriptor de seguridad del objeto SCP.
- Establecer las entradas ACE adecuadas en la lista de control de acceso discrecional (DACL) del descriptor de seguridad.
- Modificar el descriptor de seguridad del objeto 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;
}