You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
432 lines
10 KiB
432 lines
10 KiB
//+--------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1996 - 1999
|
|
//
|
|
// File: dstest.cpp
|
|
//
|
|
// Contents: DS ping test
|
|
//
|
|
// History: 13-Mar-98 mattt created
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "pch.cpp"
|
|
|
|
#pragma hdrstop
|
|
|
|
#include <winldap.h>
|
|
#include <ntldap.h>
|
|
#include <dsrole.h>
|
|
#include <dsgetdc.h>
|
|
|
|
#include <lmaccess.h>
|
|
#include <lmapibuf.h>
|
|
#include <cainfop.h>
|
|
#include "csldap.h"
|
|
|
|
#define __dwFILE__ __dwFILE_CERTCLIB_DSTEST_CPP__
|
|
|
|
|
|
#define DS_RETEST_SECONDS 15
|
|
|
|
HRESULT
|
|
myDoesDSExist(
|
|
IN BOOL fRetry)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
static BOOL s_fKnowDSExists = FALSE;
|
|
static HRESULT s_hrDSExists = HRESULT_FROM_WIN32(ERROR_NO_SUCH_DOMAIN);
|
|
static FILETIME s_ftNextTest = {0,0};
|
|
|
|
if (s_fKnowDSExists && (s_hrDSExists != S_OK) && fRetry)
|
|
// s_fKnowDSExists = FALSE; // force a retry
|
|
{
|
|
FILETIME ftCurrent;
|
|
GetSystemTimeAsFileTime(&ftCurrent);
|
|
|
|
// if Compare is < 0 (next < current), force retest
|
|
if (0 > CompareFileTime(&s_ftNextTest, &ftCurrent))
|
|
s_fKnowDSExists = FALSE;
|
|
}
|
|
|
|
if (!s_fKnowDSExists)
|
|
{
|
|
FILETIME ftCurrentNew;
|
|
GetSystemTimeAsFileTime(&ftCurrentNew);
|
|
|
|
// set NEXT in 100ns increments
|
|
|
|
((LARGE_INTEGER *) &s_ftNextTest)->QuadPart =
|
|
((LARGE_INTEGER *) &ftCurrentNew)->QuadPart +
|
|
(__int64) (CVT_BASE * CVT_SECONDS) * DS_RETEST_SECONDS;
|
|
|
|
// NetApi32 is delay loaded, so wrap to catch problems when it's not available
|
|
__try
|
|
{
|
|
DOMAIN_CONTROLLER_INFO *pDCI;
|
|
DSROLE_PRIMARY_DOMAIN_INFO_BASIC *pDsRole;
|
|
|
|
// ensure we're not standalone
|
|
pDsRole = NULL;
|
|
hr = DsRoleGetPrimaryDomainInformation( // Delayload wrapped
|
|
NULL,
|
|
DsRolePrimaryDomainInfoBasic,
|
|
(BYTE **) &pDsRole);
|
|
|
|
_PrintIfError(hr, "DsRoleGetPrimaryDomainInformation");
|
|
if (S_OK == hr &&
|
|
(pDsRole->MachineRole == DsRole_RoleStandaloneServer ||
|
|
pDsRole->MachineRole == DsRole_RoleStandaloneWorkstation))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_NO_SUCH_DOMAIN);
|
|
_PrintError(hr, "DsRoleGetPrimaryDomainInformation(no domain)");
|
|
}
|
|
|
|
if (NULL != pDsRole)
|
|
{
|
|
DsRoleFreeMemory(pDsRole); // Delayload wrapped
|
|
}
|
|
if (S_OK == hr)
|
|
{
|
|
// not standalone; return info on our DS
|
|
|
|
pDCI = NULL;
|
|
hr = DsGetDcName( // Delayload wrapped
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
DS_DIRECTORY_SERVICE_PREFERRED,
|
|
&pDCI);
|
|
_PrintIfError(hr, "DsGetDcName");
|
|
|
|
if (S_OK == hr && 0 == (pDCI->Flags & DS_DS_FLAG))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_CANT_ACCESS_DOMAIN_INFO);
|
|
_PrintError(hr, "DsGetDcName(no domain info)");
|
|
}
|
|
if (NULL != pDCI)
|
|
{
|
|
NetApiBufferFree(pDCI); // Delayload wrapped
|
|
}
|
|
}
|
|
s_fKnowDSExists = TRUE;
|
|
}
|
|
__except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
}
|
|
|
|
// else just allow users without netapi flounder with timeouts
|
|
// if ds not available...
|
|
|
|
s_hrDSExists = myHError(hr);
|
|
_PrintIfError2(
|
|
s_hrDSExists,
|
|
"DsRoleGetPrimaryDomainInformation/DsGetDcName",
|
|
HRESULT_FROM_WIN32(ERROR_NETWORK_UNREACHABLE));
|
|
}
|
|
return(s_hrDSExists);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myRobustLdapBind(
|
|
OUT LDAP **ppldap,
|
|
IN BOOL dwFlags) // RLBF_* -- must be BOOL to preserve signature
|
|
{
|
|
DWORD dwFlags1 = 0;
|
|
|
|
// for backward compatibility, TRUE implies RLBF_REQUIRE_GC
|
|
|
|
CSASSERT(TRUE == RLBF_TRUE);
|
|
if (RLBF_TRUE & dwFlags)
|
|
{
|
|
dwFlags |= RLBF_REQUIRE_GC;
|
|
dwFlags &= ~RLBF_TRUE;
|
|
dwFlags1 |= RLBF_TRUE;
|
|
}
|
|
return(myRobustLdapBindEx(
|
|
dwFlags1, // dwFlags1
|
|
dwFlags, // dwFlags2
|
|
LDAP_VERSION2,
|
|
NULL,
|
|
ppldap,
|
|
NULL));
|
|
}
|
|
|
|
|
|
HRESULT
|
|
DCSupportsSigning(
|
|
IN LDAP *pld,
|
|
OUT BOOL *pfSigningSupported)
|
|
{
|
|
HRESULT hr;
|
|
LDAPMessage *pSearchResult = NULL;
|
|
LDAPMessage *pEntry;
|
|
LDAP_TIMEVAL timeval;
|
|
WCHAR **rgpwszValues;
|
|
WCHAR *apwszAttrArray[2];
|
|
WCHAR *pwszSupportedCapabilities = LDAP_OPATT_SUPPORTED_CAPABILITIES_W;
|
|
|
|
*pfSigningSupported = FALSE;
|
|
|
|
// Query for the ldap server oerational attributes to obtain the default
|
|
// naming context.
|
|
|
|
apwszAttrArray[0] = pwszSupportedCapabilities;
|
|
apwszAttrArray[1] = NULL; // this is the sentinel
|
|
|
|
timeval.tv_sec = csecLDAPTIMEOUT;
|
|
timeval.tv_usec = 0;
|
|
|
|
hr = ldap_search_st(
|
|
pld,
|
|
NULL, // base
|
|
LDAP_SCOPE_BASE,
|
|
L"objectClass=*",
|
|
apwszAttrArray,
|
|
FALSE, // attrsonly
|
|
&timeval,
|
|
&pSearchResult);
|
|
hr = myHLdapError(pld, hr, NULL);
|
|
_JumpIfError(hr, error, "ldap_search_st");
|
|
|
|
pEntry = ldap_first_entry(pld, pSearchResult);
|
|
if (NULL == pEntry)
|
|
{
|
|
hr = myHLdapLastError(pld, NULL);
|
|
_JumpError(hr, error, "ldap_first_entry");
|
|
}
|
|
|
|
rgpwszValues = ldap_get_values(pld, pEntry, pwszSupportedCapabilities);
|
|
if (NULL != rgpwszValues)
|
|
{
|
|
DWORD i;
|
|
|
|
for (i = 0; NULL != rgpwszValues[i]; i++)
|
|
{
|
|
if (0 == wcscmp(
|
|
rgpwszValues[i],
|
|
LDAP_CAP_ACTIVE_DIRECTORY_LDAP_INTEG_OID_W))
|
|
{
|
|
*pfSigningSupported = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
ldap_value_free(rgpwszValues);
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pSearchResult)
|
|
{
|
|
ldap_msgfree(pSearchResult);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myRobustLdapBindEx(
|
|
IN BOOL dwFlags1, // TRUE --> RLBF_REQUIRE_GC -- both Flags must be BOOL
|
|
IN BOOL dwFlags2, // RLBF_* -- TRUE --> RLBF_ATTEMPT_REDISCOVER
|
|
IN ULONG uVersion,
|
|
OPTIONAL IN WCHAR const *pwszDomainName,
|
|
OUT LDAP **ppldap,
|
|
OPTIONAL OUT WCHAR **ppwszForestDNSName)
|
|
{
|
|
HRESULT hr;
|
|
BOOL fGC;
|
|
BOOL fRediscover;
|
|
ULONG ldaperr;
|
|
DWORD GetDSNameFlags;
|
|
LDAP *pld = NULL;
|
|
DWORD LDAPFlags = myGetLDAPFlags();
|
|
|
|
if (RLBF_TRUE & dwFlags1)
|
|
{
|
|
dwFlags2 |= RLBF_REQUIRE_GC;
|
|
}
|
|
if (RLBF_TRUE & dwFlags2)
|
|
{
|
|
dwFlags2 |= RLBF_ATTEMPT_REDISCOVER;
|
|
}
|
|
fGC = (RLBF_REQUIRE_GC & dwFlags2)? TRUE : FALSE;
|
|
fRediscover = (RLBF_ATTEMPT_REDISCOVER & dwFlags2)? TRUE : FALSE;
|
|
|
|
GetDSNameFlags = DS_RETURN_DNS_NAME;
|
|
if (fGC)
|
|
{
|
|
GetDSNameFlags |= DS_GC_SERVER_REQUIRED;
|
|
}
|
|
|
|
// bind to ds
|
|
|
|
while (TRUE)
|
|
{
|
|
|
|
if (NULL != pld)
|
|
{
|
|
ldap_unbind(pld);
|
|
}
|
|
|
|
pld = ldap_init(
|
|
const_cast<WCHAR *>(pwszDomainName),
|
|
(LDAPF_SSLENABLE & LDAPFlags)?
|
|
(fGC? LDAP_SSL_GC_PORT : LDAP_SSL_PORT) :
|
|
(fGC? LDAP_GC_PORT : LDAP_PORT));
|
|
if (NULL == pld)
|
|
{
|
|
hr = myHLdapLastError(NULL, NULL);
|
|
if (!fRediscover)
|
|
{
|
|
_PrintError2(hr, "ldap_init", hr);
|
|
fRediscover = TRUE;
|
|
continue;
|
|
}
|
|
_JumpError(hr, error, "ldap_init");
|
|
}
|
|
|
|
if (fRediscover)
|
|
{
|
|
GetDSNameFlags |= DS_FORCE_REDISCOVERY;
|
|
}
|
|
ldaperr = ldap_set_option(
|
|
pld,
|
|
LDAP_OPT_GETDSNAME_FLAGS,
|
|
(VOID *) &GetDSNameFlags);
|
|
if (LDAP_SUCCESS != ldaperr)
|
|
{
|
|
hr = myHLdapError(pld, ldaperr, NULL);
|
|
if (!fRediscover)
|
|
{
|
|
_PrintError2(hr, "ldap_set_option", hr);
|
|
fRediscover = TRUE;
|
|
continue;
|
|
}
|
|
_JumpError(hr, error, "ldap_set_option");
|
|
}
|
|
|
|
// if uVersion is 0, turn on TCP_KEEPALIVE
|
|
|
|
if (0 == uVersion)
|
|
{
|
|
ldaperr = ldap_set_option(pld, LDAP_OPT_TCP_KEEPALIVE, LDAP_OPT_ON);
|
|
if (LDAP_SUCCESS != ldaperr)
|
|
{
|
|
hr = myHLdapError(pld, ldaperr, NULL);
|
|
if (!fRediscover)
|
|
{
|
|
_PrintError2(hr, "ldap_set_option", hr);
|
|
fRediscover = TRUE;
|
|
continue;
|
|
}
|
|
_JumpError2(hr, error, "ldap_set_option", hr);
|
|
}
|
|
|
|
// set the uVersion to LDAP_VERSION3
|
|
|
|
uVersion = LDAP_VERSION3;
|
|
}
|
|
|
|
|
|
// set the client version. No need to set LDAP_VERSION2 since
|
|
// this is the default
|
|
|
|
if (LDAP_VERSION2 != uVersion)
|
|
{
|
|
ldaperr = ldap_set_option(pld, LDAP_OPT_VERSION, &uVersion);
|
|
if (LDAP_SUCCESS != ldaperr)
|
|
{
|
|
hr = myHLdapError(pld, ldaperr, NULL);
|
|
if (!fRediscover)
|
|
{
|
|
_PrintError2(hr, "ldap_set_option", hr);
|
|
fRediscover = TRUE;
|
|
continue;
|
|
}
|
|
_JumpError(hr, error, "ldap_set_option");
|
|
}
|
|
}
|
|
|
|
if (0 == (LDAPF_SIGNDISABLE & LDAPFlags) && IsWhistler())
|
|
{
|
|
BOOL fSigningSupported = TRUE;
|
|
|
|
// if caller requires the related DS bug fix...
|
|
|
|
if (RLBF_REQUIRE_LDAP_INTEG & dwFlags2)
|
|
{
|
|
hr = DCSupportsSigning(pld, &fSigningSupported);
|
|
_JumpIfError(hr, error, "DCSupportsSigning");
|
|
}
|
|
if (fSigningSupported)
|
|
{
|
|
ldaperr = ldap_set_option(pld, LDAP_OPT_SIGN, LDAP_OPT_ON);
|
|
if (LDAP_SUCCESS != ldaperr)
|
|
{
|
|
hr = myHLdapError2(pld, ldaperr, LDAP_PARAM_ERROR, NULL);
|
|
if (!fRediscover)
|
|
{
|
|
_PrintError2(hr, "ldap_set_option", hr);
|
|
fRediscover = TRUE;
|
|
continue;
|
|
}
|
|
_JumpError(hr, error, "ldap_set_option");
|
|
}
|
|
}
|
|
else
|
|
if (0 == (LDAPF_SSLENABLE & LDAPFlags) &&
|
|
(RLBF_REQUIRE_SECURE_LDAP & dwFlags2))
|
|
{
|
|
hr = CERTSRV_E_DOWNLEVEL_DC_SSL_OR_UPGRADE;
|
|
_JumpError(hr, error, "server missing required service pack");
|
|
}
|
|
}
|
|
|
|
ldaperr = ldap_bind_s(pld, NULL, NULL, LDAP_AUTH_NEGOTIATE);
|
|
if (LDAP_SUCCESS != ldaperr)
|
|
{
|
|
hr = myHLdapError(pld, ldaperr, NULL);
|
|
if (!fRediscover)
|
|
{
|
|
_PrintError2(hr, "ldap_bind_s", hr);
|
|
fRediscover = TRUE;
|
|
continue;
|
|
}
|
|
_JumpError(hr, error, "ldap_bind_s");
|
|
}
|
|
|
|
if (NULL != ppwszForestDNSName)
|
|
{
|
|
WCHAR *pwszDomainControllerName;
|
|
|
|
hr = myLdapGetDSHostName(pld, &pwszDomainControllerName);
|
|
if (S_OK != hr)
|
|
{
|
|
if (!fRediscover)
|
|
{
|
|
_PrintError2(hr, "myLdapGetDSHostName", hr);
|
|
fRediscover = TRUE;
|
|
continue;
|
|
}
|
|
_JumpError(hr, error, "myLdapGetDSHostName");
|
|
}
|
|
hr = myDupString(pwszDomainControllerName, ppwszForestDNSName);
|
|
_JumpIfError(hr, error, "myDupString");
|
|
}
|
|
break;
|
|
}
|
|
*ppldap = pld;
|
|
pld = NULL;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pld)
|
|
{
|
|
ldap_unbind(pld);
|
|
}
|
|
return(hr);
|
|
}
|