276 lines
5.8 KiB
C++
276 lines
5.8 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FILE
|
|
//
|
|
// iasntds.cpp
|
|
//
|
|
// SYNOPSIS
|
|
//
|
|
// Defines global objects and functions for the IAS NTDS API.
|
|
//
|
|
// MODIFICATION HISTORY
|
|
//
|
|
// 05/11/1998 Original version.
|
|
// 07/13/1998 Clean up header file dependencies.
|
|
// 08/25/1998 Added IASNtdsQueryUserAttributes.
|
|
// 09/02/1998 Added 'scope' parameter to IASNtdsQueryUserAttributes.
|
|
// 03/10/1999 Added IASNtdsIsNativeModeDomain.
|
|
// 03/23/1999 Retry failed searches.
|
|
// 05/11/1999 Ask for at least one attribute or else you get them all.
|
|
// 09/14/1999 Move SEARCH_TIMEOUT to LDAPConnection.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <ias.h>
|
|
#include <iasntds.h>
|
|
|
|
#include <ldapcxn.h>
|
|
#include <ntcache.h>
|
|
|
|
namespace
|
|
{
|
|
// Global object caches.
|
|
NTCache theDomains;
|
|
|
|
// Initialization reference count.
|
|
LONG refCount = 0;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
IASNtdsInitialize( VOID )
|
|
{
|
|
std::_Lockit _Lk;
|
|
|
|
++refCount;
|
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
VOID
|
|
WINAPI
|
|
IASNtdsUninitialize( VOID )
|
|
{
|
|
std::_Lockit _Lk;
|
|
|
|
if (--refCount == 0)
|
|
{
|
|
theDomains.clear();
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
IASNtdsIsNativeModeDomain(
|
|
IN PCWSTR domain
|
|
)
|
|
{
|
|
return theDomains.getMode(domain) == NTDomain::MODE_NATIVE;
|
|
}
|
|
|
|
//////////
|
|
// Retrieve a single entry from the DS. Handles all clean-up on failure.
|
|
//////////
|
|
DWORD
|
|
WINAPI
|
|
GetSingleEntry(
|
|
LDAPConnection* cxn,
|
|
PWCHAR base,
|
|
ULONG scope,
|
|
PWCHAR filter,
|
|
PWCHAR attrs[],
|
|
LDAPMessage **res
|
|
) throw ()
|
|
{
|
|
//////////
|
|
// Perform the search.
|
|
//////////
|
|
|
|
ULONG error = ldap_search_ext_sW(
|
|
*cxn,
|
|
base,
|
|
scope,
|
|
filter,
|
|
attrs,
|
|
FALSE,
|
|
NULL,
|
|
NULL,
|
|
&LDAPConnection::SEARCH_TIMEOUT,
|
|
0,
|
|
res
|
|
);
|
|
|
|
//////////
|
|
// Process the results.
|
|
//////////
|
|
|
|
if (error != LDAP_SUCCESS && error != LDAP_PARTIAL_RESULTS)
|
|
{
|
|
cxn->disable();
|
|
|
|
error = LdapMapErrorToWin32(error);
|
|
}
|
|
else if ((*res)->lm_returncode != LDAP_SUCCESS)
|
|
{
|
|
error = LdapMapErrorToWin32((*res)->lm_returncode);
|
|
}
|
|
else if (ldap_count_entries(*cxn, *res) != 1)
|
|
{
|
|
error = ERROR_NO_SUCH_USER;
|
|
}
|
|
else
|
|
{
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//////////
|
|
// The search failed, so clean-up.
|
|
//////////
|
|
|
|
ldap_msgfree(*res);
|
|
*res = NULL;
|
|
|
|
IASTraceFailure("ldap_search_ext_sW", error);
|
|
|
|
return error;
|
|
}
|
|
|
|
//////////
|
|
// Constants used for building LDAP search filters.
|
|
//////////
|
|
const WCHAR USER_FILTER_PREFIX[] = L"(sAMAccountName=";
|
|
const WCHAR USER_FILTER_SUFFIX[] = L")";
|
|
const size_t MAX_USERNAME_LENGTH = 256;
|
|
const size_t USER_FILTER_LENGTH = sizeof(USER_FILTER_PREFIX)/sizeof(WCHAR) +
|
|
MAX_USERNAME_LENGTH +
|
|
sizeof(USER_FILTER_SUFFIX)/sizeof(WCHAR);
|
|
|
|
//////////
|
|
// Empty attribute list.
|
|
//////////
|
|
PWCHAR NO_ATTRS[] = { L"cn", NULL };
|
|
|
|
DWORD
|
|
WINAPI
|
|
QueryUserAttributesOnce(
|
|
IN PCWSTR domainName,
|
|
IN PCWSTR username,
|
|
IN ULONG scope,
|
|
IN PWCHAR attrs[],
|
|
OUT LDAPMessage** res
|
|
)
|
|
{
|
|
//////////
|
|
// Retrieve a connection to the domain.
|
|
//////////
|
|
|
|
CComPtr<LDAPConnection> cxn;
|
|
DWORD error = theDomains.getConnection(domainName, &cxn);
|
|
|
|
switch (error)
|
|
{
|
|
case NO_ERROR:
|
|
{
|
|
IASTracePrintf("Sending LDAP search to %s.", cxn->getHost());
|
|
break;
|
|
}
|
|
|
|
case ERROR_DS_NOT_INSTALLED:
|
|
{
|
|
IASTracePrintf("DS not installed for domain %S.", domainName);
|
|
return error;
|
|
}
|
|
|
|
default:
|
|
{
|
|
IASTraceFailure("NTDomain::getConnection", error);
|
|
IASTracePrintf("Could not open an LDAP connection to domain %S.",
|
|
domainName);
|
|
return error;
|
|
}
|
|
}
|
|
|
|
//////////
|
|
// Initialize the search filter.
|
|
//////////
|
|
|
|
WCHAR searchFilter[USER_FILTER_LENGTH];
|
|
wcscpy (searchFilter, USER_FILTER_PREFIX);
|
|
wcsncat(searchFilter, username, MAX_USERNAME_LENGTH);
|
|
wcscat (searchFilter, USER_FILTER_SUFFIX);
|
|
|
|
//////////
|
|
// Query the DS. If scope == LDAP_SCOPE_BASE, then we won't retrieve the
|
|
// actual attributes yet.
|
|
//////////
|
|
|
|
error = GetSingleEntry(
|
|
cxn,
|
|
const_cast<PWCHAR>(cxn->getBase()),
|
|
LDAP_SCOPE_SUBTREE,
|
|
searchFilter,
|
|
(scope == LDAP_SCOPE_BASE ? NO_ATTRS : attrs),
|
|
res
|
|
);
|
|
|
|
if (error == NO_ERROR && scope == LDAP_SCOPE_BASE)
|
|
{
|
|
// All we care about is the user's DN.
|
|
PWCHAR dn = ldap_get_dnW(*cxn, ldap_first_entry(*cxn, *res));
|
|
ldap_msgfree(*res);
|
|
|
|
// Now get the actual attributes.
|
|
error = GetSingleEntry(
|
|
cxn,
|
|
dn,
|
|
LDAP_SCOPE_BASE,
|
|
L"(objectclass=*)",
|
|
attrs,
|
|
res
|
|
);
|
|
|
|
ldap_memfree(dn);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
IASNtdsQueryUserAttributes(
|
|
IN PCWSTR domainName,
|
|
IN PCWSTR username,
|
|
IN ULONG scope,
|
|
IN PWCHAR attrs[],
|
|
OUT LDAPMessage** res
|
|
)
|
|
{
|
|
DWORD retval = QueryUserAttributesOnce(
|
|
domainName,
|
|
username,
|
|
scope,
|
|
attrs,
|
|
res
|
|
);
|
|
|
|
switch (retval)
|
|
{
|
|
case NO_ERROR:
|
|
case ERROR_DS_NOT_INSTALLED:
|
|
case ERROR_NO_SUCH_USER:
|
|
case ERROR_ACCESS_DENIED:
|
|
return retval;
|
|
}
|
|
|
|
IASTraceString("Retrying LDAP search.");
|
|
|
|
return QueryUserAttributesOnce(
|
|
domainName,
|
|
username,
|
|
scope,
|
|
attrs,
|
|
res
|
|
);
|
|
}
|