Criando um ponto de conexão de serviço
O exemplo de código a seguir mostra como criar e inicializar um ponto de conexão de serviço. O exemplo de código executa etapas adicionais que permitem que o serviço atualize os valores SCP em tempo de execução. Normalmente, um instalador de serviço executa essas etapas como parte da instalação de uma instância de serviço em um computador host.
Este exemplo de código cria o objeto SCP como um objeto filho para o objeto do computador local. Ele usa a função GetComputerObjectName para obter o DN do objeto de computador local. Em seguida, ele usa o DN para vincular a um ponteiro IDirectoryObject para o objeto de computador. O método IDirectoryObject::CreateDSObject cria o SCP e especifica os valores iniciais para os atributos SCP importantes.
CreateDSObject retorna um ponteiro para o novo SCP, que o exemplo de código usa para recuperar um ponteiro de IADs para o SCP. O exemplo de código usa métodos IADs para recuperar os atributos objectGUID e distinguishedName do SCP. O exemplo de código usa o objectGUID para compor uma cadeia de caracteres usada para vincular ao SCP e, em seguida, armazena em cache a cadeia de caracteres de vinculação GUID no registro local, onde ela pode ser recuperada pelo serviço em tempo de execução. O distinguishedName é retornado ao chamador de função para uso na composição de um SPN (nome da entidade de serviço) para a instância de serviço.
O exemplo de código a seguir chama essa função como parte das etapas básicas de instalação de um serviço habilitado para diretório. Para obter mais informações, consulte Instalando um serviço em um computador host.
// ScpCreate
//
// Create a new service connection point as a child object of the
// local server computer object.
//
DWORD
ScpCreate(
USHORT usPort, // Service's default port to store in SCP.
LPTSTR szClass, // Service class string to store in SCP.
LPTSTR szAccount, // Logon account that must access SCP.
UINT ccDN, // Length of the pszDN buffer in characters
TCHAR *pszDN) // Returns distinguished name of SCP.
{
DWORD dwStat, dwAttr, dwLen;
HRESULT hr;
IDispatch *pDisp; // Returned dispinterface of new object.
IDirectoryObject *pComp; // Computer object; parent of SCP.
IADs *pIADsSCP; // IADs interface on new object.
if(!szClass || !szAccount || !pszDN || !(ccDN > 0))
{
hr = ERROR_INVALID_PARAMETER;
ReportError(TEXT("Invalid parameter."), hr);
return hr;
}
// Values for SCPs keywords attribute.
TCHAR* KwVal[]={
TEXT("83C29870-1DFC-11d3-A193-0000F87A9099"), // Vendor GUID.
TEXT("A762885A-AA44-11d2-81F1-00C04FB9624E"), // Product GUID.
TEXT("Microsoft"), // Vendor Name.
TEXT("Windows 2000 Auth-O-Matic"), // Product Name.
};
TCHAR szServer[MAX_PATH];
TCHAR szDn[MAX_PATH];
TCHAR szAdsPath[MAX_PATH];
TCHAR szPort[6];
HKEY hReg;
DWORD dwDisp;
ADSVALUE cn,objclass,keywords[4],binding,classname,dnsname,nametype;
// SCP attributes to set during creation of SCP.
ADS_ATTR_INFO ScpAttribs[] =
{
{
TEXT("cn"),
ADS_ATTR_UPDATE,
ADSTYPE_CASE_IGNORE_STRING,
&cn,
1
},
{
TEXT("objectClass"),
ADS_ATTR_UPDATE,
ADSTYPE_CASE_IGNORE_STRING,
&objclass,
1
},
{
TEXT("keywords"),
ADS_ATTR_UPDATE,
ADSTYPE_CASE_IGNORE_STRING,
keywords,
4
},
{
TEXT("serviceDnsName"),
ADS_ATTR_UPDATE,
ADSTYPE_CASE_IGNORE_STRING,
&dnsname,
1
},
{
TEXT("serviceDnsNameType"),
ADS_ATTR_UPDATE,
ADSTYPE_CASE_IGNORE_STRING,
&nametype,
1
},
{
TEXT("serviceClassName"),
ADS_ATTR_UPDATE,
ADSTYPE_CASE_IGNORE_STRING,
&classname,
1
},
{
TEXT("serviceBindingInformation"),
ADS_ATTR_UPDATE,
ADSTYPE_CASE_IGNORE_STRING,
&binding,
1
},
};
BSTR bstrGuid = NULL;
TCHAR pwszBindByGuidStr[1024];
VARIANT var;
// Get the DNS name of the local computer.
dwLen = sizeof(szServer);
if (!GetComputerNameEx(ComputerNameDnsFullyQualified,szServer,&dwLen))
return GetLastError();
_tprintf(TEXT("GetComputerNameEx: %s\n"), szServer);
// Enter the attribute values to be stored in the SCP.
keywords[0].dwType = ADSTYPE_CASE_IGNORE_STRING;
keywords[1].dwType = ADSTYPE_CASE_IGNORE_STRING;
keywords[2].dwType = ADSTYPE_CASE_IGNORE_STRING;
keywords[3].dwType = ADSTYPE_CASE_IGNORE_STRING;
keywords[0].CaseIgnoreString=KwVal[0];
keywords[1].CaseIgnoreString=KwVal[1];
keywords[2].CaseIgnoreString=KwVal[2];
keywords[3].CaseIgnoreString=KwVal[3];
cn.dwType = ADSTYPE_CASE_IGNORE_STRING;
cn.CaseIgnoreString = TEXT("SockAuthAD");
objclass.dwType = ADSTYPE_CASE_IGNORE_STRING;
objclass.CaseIgnoreString = TEXT("serviceConnectionPoint");
dnsname.dwType = ADSTYPE_CASE_IGNORE_STRING;
dnsname.CaseIgnoreString = szServer;
classname.dwType = ADSTYPE_CASE_IGNORE_STRING;
classname.CaseIgnoreString = szClass;
_stprintf_s(szPort,TEXT("%d"),usPort);
binding.dwType = ADSTYPE_CASE_IGNORE_STRING;
binding.CaseIgnoreString = szPort;
nametype.dwType = ADSTYPE_CASE_IGNORE_STRING;
nametype.CaseIgnoreString = TEXT("A");
/*
Get the distinguished name of the computer object for the local
computer.
*/
dwLen = sizeof(szDn);
if (!GetComputerObjectName(NameFullyQualifiedDN,szDn,&dwLen))
return GetLastError();
_tprintf(TEXT("GetComputerObjectName: %s\n"), szDn);
/*
Compose the ADSpath and bind to the computer object for the local
computer.
*/
_tcsncpy_s(szAdsPath,TEXT("LDAP://"),MAX_PATH);
_tcsncat_s(szAdsPath,szDn,MAX_PATH - _tcslen(szAdsPath));
hr = ADsGetObject(szAdsPath, IID_IDirectoryObject, (void **)&pComp);
if (FAILED(hr)) {
ReportError(TEXT("Failed to bind Computer Object."),hr);
return hr;
}
//*******************************************************************
// Publish the SCP as a child of the computer object
//*******************************************************************
// Calculate attribute count.
dwAttr = sizeof(ScpAttribs)/sizeof(ADS_ATTR_INFO);
// Complete the action.
hr = pComp->CreateDSObject(TEXT("cn=SockAuthAD"),
ScpAttribs, dwAttr, &pDisp);
if (FAILED(hr)) {
ReportError(TEXT("Failed to create SCP:"), hr);
pComp -> Release();
return hr;
}
pComp -> Release();
// Query for an IADs pointer on the SCP object.
hr = pDisp->QueryInterface(IID_IADs,(void **)&pIADsSCP);
if (FAILED(hr)) {
ReportError(TEXT("Failed to QueryInterface for IADs:"),hr);
pDisp->Release();
return hr;
}
pDisp->Release();
// Set ACEs on the SCP so a service can modify it.
hr = AllowAccessToScpProperties(
szAccount, // Service account to allow access.
pIADsSCP); // IADs pointer to the SCP object.
if (FAILED(hr)) {
ReportError(TEXT("Failed to set ACEs on SCP DACL:"), hr);
return hr;
}
// Get the distinguished name of the SCP.
VariantInit(&var);
hr = pIADsSCP->Get(CComBSTR("distinguishedName"), &var);
if (FAILED(hr)) {
ReportError(TEXT("Failed to get distinguishedName:"), hr);
pIADsSCP->Release();
return hr;
}
_tprintf(TEXT("distinguishedName via IADs: %s\n"), var.bstrVal);
// Return the DN of the SCP, which is used to compose the SPN.
// The best practice is to either accept and return the buffer
// size or do this in a _try / _except block, both omitted here
// for clarity.
_tcsncpy_s(pszDN, var.bstrVal, ccDN);
// Retrieve the SCP objectGUID in format suitable for binding.
hr = pIADsSCP->get_GUID(&bstrGuid);
if (FAILED(hr)) {
ReportError(TEXT("Failed to get GUID:"), hr);
pIADsSCP->Release();
return hr;
}
// Build a string for binding to the object by GUID.
_tcsncpy_s(pwszBindByGuidStr,
TEXT("LDAP://<GUID="),
1024);
_tcsncat_s(pwszBindByGuidStr,
bstrGuid,
1024 -_tcslen(pwszBindByGuidStr));
_tcsncat_s(pwszBindByGuidStr,
TEXT(">"),
1024 -_tcslen(pwszBindByGuidStr));
_tprintf(TEXT("GUID binding string: %s\n"),
pwszBindByGuidStr);
pIADsSCP->Release();
// Create a registry key under
// HKEY_LOCAL_MACHINE\SOFTWARE\Vendor\Product.
dwStat = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
TEXT("Software\\Fabrikam\\Auth-O-Matic"),
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hReg,
&dwDisp);
if (dwStat != NO_ERROR) {
ReportError(TEXT("RegCreateKeyEx failed:"), dwStat);
return dwStat;
}
// Cache the GUID binding string under the registry key.
dwStat = RegSetValueEx(hReg, TEXT("GUIDBindingString"), 0, REG_SZ,
(const BYTE *)pwszBindByGuidStr,
2*(_tcslen(pwszBindByGuidStr)));
if (dwStat != NO_ERROR) {
ReportError(TEXT("RegSetValueEx failed:"), dwStat);
return dwStat;
}
RegCloseKey(hReg);
// Cleanup should delete the SCP and registry key if an error occurs.
return dwStat;
}