724 lines
20 KiB
C
724 lines
20 KiB
C
|
//++
|
||
|
//
|
||
|
// Copyright (C) Microsoft Corporation, 1987 - 1999
|
||
|
//
|
||
|
// Module Name:
|
||
|
//
|
||
|
// ldaptest.c
|
||
|
//
|
||
|
// Abstract:
|
||
|
//
|
||
|
// Queries into network drivers
|
||
|
//
|
||
|
// Author:
|
||
|
//
|
||
|
// Anilth - 4-20-1998
|
||
|
//
|
||
|
// Environment:
|
||
|
//
|
||
|
// User mode only.
|
||
|
// Contains NT-specific code.
|
||
|
//
|
||
|
// Revision History:
|
||
|
//
|
||
|
//--
|
||
|
|
||
|
#include "precomp.h"
|
||
|
#include "malloc.h"
|
||
|
|
||
|
BOOL TestLdapOnDc(IN PTESTED_DC TestedDc, NETDIAG_PARAMS* pParams, NETDIAG_RESULT* pResults);
|
||
|
DWORD TestSpnOnDC(IN PTESTED_DC pDcInfo, NETDIAG_RESULT* pResults);
|
||
|
|
||
|
HRESULT LDAPTest( NETDIAG_PARAMS* pParams, NETDIAG_RESULT* pResults)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
BOOL RetVal = TRUE;
|
||
|
PTESTED_DOMAIN TestedDomain = pParams->pDomain;
|
||
|
PTESTED_DC TestedDc = NULL;
|
||
|
PLIST_ENTRY ListEntry;
|
||
|
BOOLEAN OneLdapFailed = FALSE;
|
||
|
BOOLEAN OneLdapWorked = FALSE;
|
||
|
BOOL fSpnTested = FALSE;
|
||
|
BOOL fSpnPassed = FALSE;
|
||
|
|
||
|
NET_API_STATUS NetStatus;
|
||
|
|
||
|
//if the machine is a member machine or DC, LDAP Test will get called.
|
||
|
//Otherwise, the test will be skipped
|
||
|
pResults->LDAP.fPerformed = TRUE;
|
||
|
|
||
|
// assume link entry is initialized to 0000
|
||
|
if(pResults->LDAP.lmsgOutput.Flink == NULL)
|
||
|
InitializeListHead(&pResults->LDAP.lmsgOutput);
|
||
|
|
||
|
PrintStatusMessage(pParams, 4, IDS_LDAP_STATUS_MSG, TestedDomain->PrintableDomainName);
|
||
|
|
||
|
//
|
||
|
// If a DC hasn't been discovered yet,
|
||
|
// find one.
|
||
|
//
|
||
|
|
||
|
if ( TestedDomain->DcInfo == NULL )
|
||
|
{
|
||
|
LPTSTR pszDcType;
|
||
|
|
||
|
if ( TestedDomain->fTriedToFindDcInfo )
|
||
|
{
|
||
|
CHK_HR_CONTEXT(pResults->LDAP, S_FALSE, IDS_LDAP_NODC);
|
||
|
}
|
||
|
|
||
|
pszDcType = LoadAndAllocString(IDS_DCTYPE_DC);
|
||
|
NetStatus = DoDsGetDcName( pParams,
|
||
|
pResults,
|
||
|
&pResults->LDAP.lmsgOutput,
|
||
|
TestedDomain,
|
||
|
DS_DIRECTORY_SERVICE_PREFERRED,
|
||
|
"DC",
|
||
|
FALSE,
|
||
|
&TestedDomain->DcInfo );
|
||
|
Free(pszDcType);
|
||
|
|
||
|
TestedDomain->fTriedToFindDcInfo = TRUE;
|
||
|
|
||
|
if ( NetStatus != NO_ERROR )
|
||
|
{
|
||
|
CHK_HR_CONTEXT(pResults->LDAP, hr = HRESULT_FROM_WIN32(NetStatus), IDS_LDAP_NODC);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Ensure the DC is running the Ds.
|
||
|
//
|
||
|
|
||
|
if ( (TestedDomain->DcInfo->Flags & DS_DS_FLAG) == 0 )
|
||
|
{
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_Quiet, 0, IDS_LDAP_NOTRUNNINGDS,
|
||
|
TestedDomain->DcInfo->DomainControllerName);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Test ldap on all of the found DCs in the domain.
|
||
|
//
|
||
|
|
||
|
for ( ListEntry = TestedDomain->TestedDcs.Flink ;
|
||
|
ListEntry != &TestedDomain->TestedDcs ;
|
||
|
ListEntry = ListEntry->Flink ) {
|
||
|
|
||
|
|
||
|
//
|
||
|
// Loop through the list of DCs in this domain
|
||
|
//
|
||
|
|
||
|
TestedDc = CONTAINING_RECORD( ListEntry, TESTED_DC, Next );
|
||
|
|
||
|
//
|
||
|
// Only run test on DCs that might support LDAP.
|
||
|
//
|
||
|
|
||
|
if ( TestedDc->Flags & DC_IS_NT5 )
|
||
|
{
|
||
|
if ( !TestLdapOnDc( TestedDc, pParams, pResults ) )
|
||
|
OneLdapFailed = TRUE;
|
||
|
else
|
||
|
OneLdapWorked = TRUE;
|
||
|
|
||
|
//test the SPN registration if this is a DC on the primary domain
|
||
|
if (TestedDomain->fPrimaryDomain)
|
||
|
{
|
||
|
fSpnTested = TRUE;
|
||
|
if (TestSpnOnDC(TestedDc, pResults))
|
||
|
fSpnPassed = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_ReallyVerbose, 0,
|
||
|
IDS_LDAP_NOTRUNNINGDS_SKIP, TestedDc->ComputerName);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If one of the DCs failed,
|
||
|
// and none worked.
|
||
|
// Don't do any more tests.
|
||
|
//
|
||
|
if ( OneLdapFailed && !OneLdapWorked )
|
||
|
{
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_Quiet, 0,
|
||
|
IDS_LDAP_NOLDAPSERVERSWORK, TestedDomain->PrintableDomainName);
|
||
|
|
||
|
CHK_HR_CONTEXT(pResults->LDAP, hr = E_FAIL, 0);
|
||
|
}
|
||
|
|
||
|
if ( fSpnTested && !fSpnPassed )
|
||
|
{
|
||
|
//IDS_LDAP_NO_SPN " [FATAL] The default SPNs are not properly registered on and DCs.\n"
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_Quiet, 0,
|
||
|
IDS_LDAP_NO_SPN);
|
||
|
CHK_HR_CONTEXT(pResults->LDAP, hr = E_FAIL, 0);
|
||
|
}
|
||
|
|
||
|
L_ERR:
|
||
|
|
||
|
//$REVIEW (nsun) we should return S_FALSE or S_OK
|
||
|
//so that we can go on with other tests
|
||
|
if (!FHrOK(hr))
|
||
|
hr = S_FALSE;
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
DWORD TestSpnOnDC(IN PTESTED_DC pDcInfo, NETDIAG_RESULT* pResults)
|
||
|
{
|
||
|
WCHAR FlatName[ MAX_PATH + 1 ];
|
||
|
PWSTR Dot ;
|
||
|
HANDLE hDs = NULL;
|
||
|
ULONG NetStatus ;
|
||
|
PDS_NAME_RESULTW Result ;
|
||
|
LPWSTR Flat = FlatName;
|
||
|
LDAP *ld = NULL;
|
||
|
int rc;
|
||
|
LDAPMessage *e, *res;
|
||
|
WCHAR *base_dn;
|
||
|
WCHAR *search_dn, search_ava[ MAX_PATH + 30 ];
|
||
|
WCHAR Domain[ MAX_PATH + 1 ];
|
||
|
CHAR szDefaultFqdnSpn[MAX_PATH + 10];
|
||
|
CHAR szDefaultShortSpn[MAX_PATH + 10];
|
||
|
BOOL fFqdnSpnFound = FALSE;
|
||
|
BOOL fShortSpnFound = FALSE;
|
||
|
BOOL fFailQuerySpn = FALSE;
|
||
|
|
||
|
USES_CONVERSION;
|
||
|
|
||
|
//construct the default SPN's
|
||
|
lstrcpy(szDefaultFqdnSpn, _T("HOST/"));
|
||
|
lstrcat(szDefaultFqdnSpn, pResults->Global.szDnsHostName);
|
||
|
lstrcpy(szDefaultShortSpn, _T("HOST/"));
|
||
|
lstrcat(szDefaultShortSpn, W2T(pResults->Global.swzNetBiosName));
|
||
|
|
||
|
wcscpy(Domain, GetSafeStringW(pDcInfo->TestedDomain->DnsDomainName ?
|
||
|
pDcInfo->TestedDomain->DnsDomainName :
|
||
|
pDcInfo->TestedDomain->NetbiosDomainName));
|
||
|
|
||
|
ld = ldap_open(W2A(pDcInfo->ComputerName), LDAP_PORT);
|
||
|
if (ld == NULL) {
|
||
|
DebugMessage2("ldap_init failed = %x", LdapGetLastError());
|
||
|
fFailQuerySpn = TRUE;
|
||
|
goto L_ERROR;
|
||
|
}
|
||
|
|
||
|
rc = ldap_bind_s(ld, NULL, NULL, LDAP_AUTH_NEGOTIATE);
|
||
|
if (rc != LDAP_SUCCESS) {
|
||
|
DebugMessage2("ldap_bind failed = %x", LdapGetLastError());
|
||
|
fFailQuerySpn = TRUE;
|
||
|
goto L_ERROR;
|
||
|
|
||
|
}
|
||
|
|
||
|
NetStatus = DsBindW( NULL, Domain, &hDs );
|
||
|
if ( NetStatus != 0 )
|
||
|
{
|
||
|
DebugMessage3("Failed to bind to DC of domain %ws, %#x\n",
|
||
|
Domain, NetStatus );
|
||
|
fFailQuerySpn = TRUE;
|
||
|
goto L_ERROR;
|
||
|
}
|
||
|
|
||
|
Dot = wcschr( Domain, L'.' );
|
||
|
|
||
|
if ( Dot )
|
||
|
{
|
||
|
*Dot = L'\0';
|
||
|
}
|
||
|
|
||
|
wcscpy( FlatName, Domain );
|
||
|
|
||
|
if ( Dot )
|
||
|
{
|
||
|
*Dot = L'.' ;
|
||
|
}
|
||
|
|
||
|
wcscat( FlatName, L"\\" );
|
||
|
wcscat( FlatName, pResults->Global.swzNetBiosName );
|
||
|
wcscat( FlatName, L"$" );
|
||
|
|
||
|
NetStatus = DsCrackNamesW(
|
||
|
hDs,
|
||
|
0,
|
||
|
DS_NT4_ACCOUNT_NAME,
|
||
|
DS_FQDN_1779_NAME,
|
||
|
1,
|
||
|
&Flat,
|
||
|
&Result );
|
||
|
|
||
|
if ( NetStatus != 0)
|
||
|
{
|
||
|
DebugMessage3("Failed to crack name %ws into the FQDN, %#x\n",
|
||
|
FlatName, NetStatus );
|
||
|
|
||
|
DsUnBind( &hDs );
|
||
|
|
||
|
fFailQuerySpn = TRUE;
|
||
|
goto L_ERROR;
|
||
|
}
|
||
|
|
||
|
search_dn = pResults->Global.swzNetBiosName;
|
||
|
|
||
|
if (0 == Result->cItems)
|
||
|
{
|
||
|
DsUnBind( &hDs );
|
||
|
|
||
|
fFailQuerySpn = TRUE;
|
||
|
goto L_ERROR;
|
||
|
}
|
||
|
|
||
|
if (DS_NAME_NO_ERROR != Result->rItems[0].status || NULL == Result->rItems[0].pName)
|
||
|
{
|
||
|
DsUnBind( &hDs );
|
||
|
|
||
|
fFailQuerySpn = TRUE;
|
||
|
goto L_ERROR;
|
||
|
}
|
||
|
|
||
|
base_dn = wcschr(Result->rItems[0].pName, L',');
|
||
|
|
||
|
if (!base_dn)
|
||
|
base_dn = Result->rItems[0].pName;
|
||
|
else
|
||
|
base_dn++;
|
||
|
|
||
|
DsUnBind( &hDs );
|
||
|
|
||
|
swprintf(search_ava, L"(sAMAccountName=%s$)", search_dn);
|
||
|
|
||
|
rc = ldap_search_s(ld, W2A(base_dn), LDAP_SCOPE_SUBTREE,
|
||
|
W2A(search_ava), NULL, 0, &res);
|
||
|
|
||
|
//base_dn can no longer be used because base_dn refers to that buffer
|
||
|
DsFreeNameResultW( Result );
|
||
|
|
||
|
if (rc != LDAP_SUCCESS) {
|
||
|
DebugMessage2("ldap_search_s failed: %s", ldap_err2string(rc));
|
||
|
fFailQuerySpn = TRUE;
|
||
|
goto L_ERROR;
|
||
|
}
|
||
|
|
||
|
for (e = ldap_first_entry(ld, res);
|
||
|
e;
|
||
|
e = ldap_next_entry(ld, e))
|
||
|
{
|
||
|
BerElement *b;
|
||
|
CHAR *attr;
|
||
|
|
||
|
//IDS_LDAP_REG_SPN "Registered Service Principal Names:\n"
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_ReallyVerbose, 0,
|
||
|
IDS_LDAP_REG_SPN);
|
||
|
|
||
|
for (attr = ldap_first_attribute(ld, e, &b);
|
||
|
attr;
|
||
|
attr = ldap_next_attribute(ld, e, b))
|
||
|
{
|
||
|
CHAR **values, **p;
|
||
|
values = ldap_get_values(ld, e, attr);
|
||
|
for (p = values; *p; p++)
|
||
|
{
|
||
|
if (strcmp(attr, "servicePrincipalName") == 0)
|
||
|
{
|
||
|
// IDS_LDAP_SPN_NAME " %s\n"
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_ReallyVerbose, 0,
|
||
|
IDS_LDAP_SPN_NAME, *p);
|
||
|
|
||
|
if (0 == _stricmp(*p, szDefaultFqdnSpn))
|
||
|
fFqdnSpnFound = TRUE;
|
||
|
else if (0 == _stricmp(*p, szDefaultShortSpn))
|
||
|
fShortSpnFound = TRUE;
|
||
|
}
|
||
|
}
|
||
|
ldap_value_free(values);
|
||
|
ldap_memfree(attr);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
ldap_msgfree(res);
|
||
|
|
||
|
L_ERROR:
|
||
|
if (ld)
|
||
|
{
|
||
|
ldap_unbind(ld);
|
||
|
}
|
||
|
|
||
|
//Only report fatal error when we successfully query SPN registration
|
||
|
//and all DCs doesn't have the default SPN's
|
||
|
if (fFailQuerySpn)
|
||
|
{
|
||
|
//IDS_LDAP_SPN_FAILURE "Failed to query SPN registration from DC %ws.\n"
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_Quiet, 0,
|
||
|
IDS_LDAP_SPN_FAILURE, pDcInfo->ComputerName);
|
||
|
return TRUE;
|
||
|
}
|
||
|
else if (!fFqdnSpnFound || !fShortSpnFound)
|
||
|
{
|
||
|
//IDS_LDAP_SPN_MISSING " [WARNING] The default SPN registration for '%s' is missing on DC '%ws'.\n"
|
||
|
if (!fFqdnSpnFound)
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_Quiet, 0,
|
||
|
IDS_LDAP_SPN_MISSING, szDefaultFqdnSpn, pDcInfo->ComputerName);
|
||
|
|
||
|
if (!fShortSpnFound)
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_Quiet, 0,
|
||
|
IDS_LDAP_SPN_MISSING, szDefaultShortSpn, pDcInfo->ComputerName);
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
else
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
void LDAPGlobalPrint(IN NETDIAG_PARAMS *pParams, IN OUT NETDIAG_RESULT *pResults)
|
||
|
{
|
||
|
if (pParams->fVerbose || !FHrOK(pResults->LDAP.hr))
|
||
|
{
|
||
|
PrintNewLine(pParams, 2);
|
||
|
PrintTestTitleResult(pParams, IDS_LDAP_LONG, IDS_LDAP_SHORT, pResults->LDAP.fPerformed,
|
||
|
pResults->LDAP.hr, 0);
|
||
|
|
||
|
if (!FHrOK(pResults->LDAP.hr))
|
||
|
{
|
||
|
if(pResults->LDAP.idsContext)
|
||
|
PrintError(pParams, pResults->LDAP.idsContext, pResults->LDAP.hr);
|
||
|
}
|
||
|
|
||
|
PrintMessageList(pParams, &pResults->LDAP.lmsgOutput);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
void LDAPPerInterfacePrint(IN NETDIAG_PARAMS *pParams,
|
||
|
IN OUT NETDIAG_RESULT *pResults,
|
||
|
IN INTERFACE_RESULT *pIfResult)
|
||
|
{
|
||
|
// no perinterface information
|
||
|
}
|
||
|
|
||
|
void LDAPCleanup(IN NETDIAG_PARAMS *pParams,
|
||
|
IN OUT NETDIAG_RESULT *pResults)
|
||
|
{
|
||
|
MessageListCleanUp(&pResults->LDAP.lmsgOutput);
|
||
|
pResults->LDAP.lmsgOutput.Flink = NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
TestLdapOnDc(
|
||
|
IN PTESTED_DC TestedDc,NETDIAG_PARAMS* pParams, NETDIAG_RESULT* pResults
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
|
||
|
Ensure we can use LDAP focused at the specified DC
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
TestedDc - Description of DC to test
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE: Test suceeded.
|
||
|
FALSE: Test failed
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
NET_API_STATUS NetStatus;
|
||
|
NTSTATUS Status;
|
||
|
BOOL RetVal = TRUE;
|
||
|
int LdapMessageId;
|
||
|
PLDAPMessage LdapMessage = NULL;
|
||
|
PLDAPMessage CurrentEntry;
|
||
|
int LdapError;
|
||
|
ULONG AuthType;
|
||
|
LPSTR AuthTypeName;
|
||
|
LPWSTR DcIpAddress;
|
||
|
|
||
|
LDAP *LdapHandle = NULL;
|
||
|
|
||
|
//
|
||
|
// Avoid this test if the DC is already known to be down.
|
||
|
//
|
||
|
|
||
|
if ( TestedDc->Flags & DC_IS_DOWN ) {
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_ReallyVerbose, 0,
|
||
|
IDS_LDAP_DCDOWN, TestedDc->ComputerName);
|
||
|
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If there is no IP Address,
|
||
|
// get it.
|
||
|
//
|
||
|
|
||
|
if ( !GetIpAddressForDc( TestedDc ) ) {
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_Quiet, 0,
|
||
|
IDS_LDAP_NOIPADDR, TestedDc->ComputerName);
|
||
|
RetVal = FALSE;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
DcIpAddress = TestedDc->DcIpAddress;
|
||
|
|
||
|
//
|
||
|
// Loop trying each type of authentication.
|
||
|
//
|
||
|
|
||
|
for ( AuthType = 0; AuthType < 3; AuthType++ ) {
|
||
|
int AuthMethod;
|
||
|
SEC_WINNT_AUTH_IDENTITY_W NtAuthIdentity;
|
||
|
LPSTR AuthGuru;
|
||
|
|
||
|
//
|
||
|
// Bind as appropropriate
|
||
|
//
|
||
|
|
||
|
RtlZeroMemory( &NtAuthIdentity, sizeof(NtAuthIdentity));
|
||
|
NtAuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
|
||
|
switch ( AuthType ) {
|
||
|
case 0:
|
||
|
AuthTypeName = "un-";
|
||
|
break;
|
||
|
case 1:
|
||
|
AuthTypeName = "NTLM ";
|
||
|
AuthMethod = LDAP_AUTH_NTLM;
|
||
|
AuthGuru = NTLM_GURU;
|
||
|
break;
|
||
|
case 2:
|
||
|
AuthTypeName = "Negotiate ";
|
||
|
AuthMethod = LDAP_AUTH_NEGOTIATE;
|
||
|
AuthGuru = KERBEROS_GURU;
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Only Members and Domain controllers can use authenticated RPC.
|
||
|
//
|
||
|
|
||
|
if ( AuthType != 0 ) {
|
||
|
if ( pResults->Global.pPrimaryDomainInfo->MachineRole == DsRole_RoleMemberWorkstation ||
|
||
|
pResults->Global.pPrimaryDomainInfo->MachineRole == DsRole_RoleMemberServer ||
|
||
|
pResults->Global.pPrimaryDomainInfo->MachineRole == DsRole_RoleBackupDomainController ||
|
||
|
pResults->Global.pPrimaryDomainInfo->MachineRole == DsRole_RolePrimaryDomainController ) {
|
||
|
|
||
|
//
|
||
|
// If we're logged onto a local account,
|
||
|
// we can't test authenticated RPC.
|
||
|
//
|
||
|
|
||
|
if ( pResults->Global.pLogonDomain == NULL ) {
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_Quiet, 0,
|
||
|
IDS_LDAP_LOGONASLOCALUSER,
|
||
|
pResults->Global.pLogonDomainName,
|
||
|
pResults->Global.pLogonUser,
|
||
|
AuthTypeName, TestedDc->ComputerName);
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_ReallyVerbose, 0,
|
||
|
IDS_LDAP_DOAUTHEN,
|
||
|
AuthTypeName, TestedDc->ComputerName);
|
||
|
|
||
|
//
|
||
|
// Cleanup from a previous iteration.
|
||
|
//
|
||
|
|
||
|
if ( LdapMessage != NULL )
|
||
|
{
|
||
|
ldap_msgfree( LdapMessage );
|
||
|
LdapMessage = NULL;
|
||
|
}
|
||
|
|
||
|
if ( LdapHandle != NULL )
|
||
|
{
|
||
|
ldap_unbind( LdapHandle );
|
||
|
LdapHandle = NULL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Connect to the DC.
|
||
|
//
|
||
|
|
||
|
LdapHandle = ldap_openW( DcIpAddress, LDAP_PORT );
|
||
|
|
||
|
if ( LdapHandle == NULL )
|
||
|
{
|
||
|
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_Quiet, 0,
|
||
|
IDS_LDAP_CANNOTOPEN,
|
||
|
TestedDc->ComputerName, DcIpAddress );
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Bind to the DC.
|
||
|
//
|
||
|
|
||
|
if ( AuthType != 0 ) {
|
||
|
LdapError = ldap_bind_s( LdapHandle, NULL, (char *)&NtAuthIdentity, AuthMethod );
|
||
|
|
||
|
if ( LdapError != LDAP_SUCCESS ) {
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_Quiet, 0,
|
||
|
IDS_LDAP_CANNOTBIND,
|
||
|
AuthTypeName, TestedDc->ComputerName, ldap_err2stringA(LdapError) );
|
||
|
|
||
|
//
|
||
|
// Try other authentication methods.
|
||
|
//
|
||
|
RetVal = FALSE;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Do a trivial search to isolate LDAP problems from authentication
|
||
|
// problems
|
||
|
//
|
||
|
|
||
|
LdapError = ldap_search_sA(
|
||
|
LdapHandle,
|
||
|
NULL, // DN
|
||
|
LDAP_SCOPE_BASE,
|
||
|
"(objectClass=*)", // filter
|
||
|
NULL,
|
||
|
FALSE,
|
||
|
&LdapMessage );
|
||
|
|
||
|
if ( LdapError != LDAP_SUCCESS ) {
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_Quiet, 0,
|
||
|
IDS_LDAP_CANNOTSEARCH,
|
||
|
AuthTypeName, TestedDc->ComputerName, ldap_err2stringA(LdapError) );
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// How many entries were returned.
|
||
|
//
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_ReallyVerbose, 0,
|
||
|
IDS_LDAP_ENTRIES,
|
||
|
ldap_count_entries( LdapHandle, LdapMessage ) );
|
||
|
|
||
|
|
||
|
//
|
||
|
// Print the entries.
|
||
|
//
|
||
|
|
||
|
CurrentEntry = ldap_first_entry( LdapHandle, LdapMessage );
|
||
|
|
||
|
while ( CurrentEntry != NULL )
|
||
|
{
|
||
|
PVOID Context;
|
||
|
char *AttrName;
|
||
|
|
||
|
//
|
||
|
// Test for error
|
||
|
//
|
||
|
if ( LdapHandle->ld_errno != 0 )
|
||
|
{
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_Quiet, 0,
|
||
|
IDS_LDAP_CANNOTFIRSTENTRY,
|
||
|
TestedDc->ComputerName, ldap_err2stringA(LdapHandle->ld_errno) );
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Walk through the list of returned attributes.
|
||
|
//
|
||
|
|
||
|
AttrName = ldap_first_attributeA( LdapHandle, CurrentEntry, (PVOID)&Context );
|
||
|
while ( AttrName != NULL )
|
||
|
{
|
||
|
PLDAP_BERVAL *Berval;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Test for error
|
||
|
//
|
||
|
|
||
|
if ( LdapHandle->ld_errno != 0 ) {
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_Quiet, 0,
|
||
|
IDS_LDAP_CANNOTFIRSTATTR,
|
||
|
TestedDc->ComputerName, ldap_err2stringA(LdapHandle->ld_errno) );
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Grab the attribute and it's value
|
||
|
//
|
||
|
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_ReallyVerbose, 0,
|
||
|
IDS_LDAP_ATTR, AttrName );
|
||
|
|
||
|
Berval = ldap_get_values_lenA( LdapHandle, CurrentEntry, AttrName );
|
||
|
|
||
|
if ( Berval == NULL )
|
||
|
{
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_Quiet, 0,
|
||
|
IDS_LDAP_CANNOTLEN,
|
||
|
TestedDc->ComputerName, ldap_err2stringA(LdapHandle->ld_errno) );
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int i;
|
||
|
for ( i=0; Berval[i] != NULL; i++ ) {
|
||
|
AddIMessageToList(&pResults->LDAP.lmsgOutput, Nd_ReallyVerbose, 0,
|
||
|
IDS_LDAP_VAL,
|
||
|
Berval[i]->bv_len, Berval[i]->bv_val );
|
||
|
}
|
||
|
ldap_value_free_len( Berval );
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Get the next entry
|
||
|
//
|
||
|
|
||
|
AttrName = ldap_next_attributeA( LdapHandle, CurrentEntry, (PVOID)Context );
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Get the next entry
|
||
|
//
|
||
|
|
||
|
CurrentEntry = ldap_next_entry( LdapHandle, CurrentEntry );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
Cleanup:
|
||
|
|
||
|
|
||
|
if ( LdapMessage != NULL ) {
|
||
|
ldap_msgfree( LdapMessage );
|
||
|
}
|
||
|
|
||
|
return RetVal;
|
||
|
}
|
||
|
|
||
|
|