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.
3940 lines
89 KiB
3940 lines
89 KiB
#include "ldapc.hxx"
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// Local helpers
|
|
//
|
|
|
|
extern "C" {
|
|
DWORD
|
|
GetDefaultLdapServer(
|
|
LPWSTR Addresses[],
|
|
LPDWORD Count,
|
|
BOOL Verify,
|
|
DWORD dwPort
|
|
) ;
|
|
|
|
}
|
|
|
|
WINLDAPAPI ULONG LDAPAPI ldap_get_optionW(
|
|
LDAP *ld,
|
|
int option,
|
|
void *outvalue
|
|
);
|
|
|
|
DWORD
|
|
GetDefaultServer(
|
|
DWORD dwPort,
|
|
BOOL fVerify,
|
|
LPWSTR szDomainDnsName,
|
|
LPWSTR szServerName,
|
|
BOOL fWriteable
|
|
);
|
|
|
|
int ConvertToAscii( WCHAR *pszUnicode, char **pszAscii );
|
|
int ConvertLDAPMod( LDAPModW **mods, LDAPModA ***modsA );
|
|
void FreeLDAPMod( LDAPModA **modsA );
|
|
BOOLEAN LDAPCodeWarrantsRetry(HRESULT hr);
|
|
|
|
|
|
HRESULT LdapSearchHelper(
|
|
LDAP *ld,
|
|
WCHAR *base,
|
|
int scope,
|
|
WCHAR *filter,
|
|
WCHAR *attrs[],
|
|
int attrsonly,
|
|
struct l_timeval *timeout,
|
|
LDAPMessage **res
|
|
);
|
|
|
|
|
|
HRESULT
|
|
GetOneToken(
|
|
LPWSTR pszSource,
|
|
LPWSTR * ppszToken,
|
|
DWORD * pdwTokenLen,
|
|
BOOL * pfMore
|
|
);
|
|
|
|
DWORD
|
|
MaskKnownFlagsNotChecked(DWORD dwFlags)
|
|
{
|
|
DWORD dwRetVal = dwFlags;
|
|
|
|
dwRetVal &= ~(ADS_PROMPT_CREDENTIALS
|
|
| ADS_FAST_BIND
|
|
| ADS_READONLY_SERVER
|
|
| ADS_USE_SIGNING
|
|
| ADS_USE_SEALING
|
|
| ADS_USE_DELEGATION
|
|
| ADS_SERVER_BIND
|
|
| ADS_AUTH_RESERVED
|
|
);
|
|
|
|
return dwRetVal;
|
|
|
|
}
|
|
|
|
|
|
LPWSTR gpszServerName = NULL;
|
|
LPWSTR gpszDomainName = NULL;
|
|
|
|
//
|
|
// High level Open/Close object functions
|
|
//
|
|
HRESULT LdapOpenObject(
|
|
LPWSTR szLDAPServer,
|
|
LPWSTR szLDAPDn,
|
|
ADS_LDP **ld,
|
|
CCredentials& Credentials,
|
|
DWORD dwPort
|
|
)
|
|
{
|
|
return LdapOpenObject2(
|
|
szLDAPServer,
|
|
NULL,
|
|
szLDAPDn,
|
|
ld,
|
|
Credentials,
|
|
dwPort
|
|
);
|
|
}
|
|
|
|
|
|
HRESULT LdapOpenObject2(
|
|
LPWSTR szDomainName,
|
|
LPWSTR szLDAPServer,
|
|
LPWSTR szLDAPDn,
|
|
ADS_LDP **ld,
|
|
CCredentials& Credentials,
|
|
DWORD dwPort
|
|
)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
int err = NO_ERROR;
|
|
LUID Luid, ModifiedId;
|
|
PADS_LDP pCacheEntry = NULL ;
|
|
BOOL fAddToCache = TRUE ;
|
|
|
|
LPWSTR aAddresses[5];
|
|
BOOL fServerNotSpecified = FALSE;
|
|
BOOL fVerify = FALSE;
|
|
WCHAR szDomainDnsName[MAX_PATH];
|
|
WCHAR szServerName[MAX_PATH];
|
|
|
|
DWORD dwFlags = Credentials.GetAuthFlags();
|
|
BOOL fStickyServerSpecified = (gpszServerName != NULL);
|
|
|
|
//
|
|
// start by nulling out this return value
|
|
//
|
|
*ld = NULL ;
|
|
|
|
//
|
|
// Now if the server name is Null - Get a serverName
|
|
//
|
|
|
|
if (!szDomainName) {
|
|
|
|
|
|
RetryGetDefaultServer:
|
|
|
|
err = GetDefaultServer(
|
|
dwPort,
|
|
fVerify,
|
|
szDomainDnsName,
|
|
szServerName,
|
|
!(dwFlags & ADS_READONLY_SERVER) ? TRUE : FALSE
|
|
);
|
|
|
|
if (err != NOERROR) {
|
|
return HRESULT_FROM_WIN32(err);
|
|
}
|
|
|
|
if (fStickyServerSpecified) {
|
|
//
|
|
// We need to change the name of the domain to be that of
|
|
// the server we want to target. The swap is made if
|
|
// 1) gpszDomainName == NULL, that implies that just
|
|
// a serverName was set and not which domain it applies to.
|
|
// 2) If a domainName is specified, then the domainName
|
|
// from above should be that set in the global pointer for
|
|
// the target server to be changed.
|
|
//
|
|
if ((gpszDomainName
|
|
&& (!_wcsicmp(szDomainDnsName, gpszDomainName))
|
|
)
|
|
|| (gpszDomainName == NULL)
|
|
) {
|
|
//
|
|
// We need to change the target to the server.
|
|
//
|
|
wcscpy(szDomainDnsName,gpszServerName);
|
|
szServerName[0] = L'\0';
|
|
//
|
|
// Make sure if server is down we go to another
|
|
// server on the retryGetDefault server path.
|
|
//
|
|
fStickyServerSpecified = FALSE;
|
|
}
|
|
}
|
|
|
|
szDomainName = szDomainDnsName;
|
|
szLDAPServer = szServerName;
|
|
fServerNotSpecified = TRUE;
|
|
}
|
|
|
|
#ifndef WIN95
|
|
//
|
|
// try the cache first. if find entry, just use it (this bumps ref count
|
|
// up by one). if we cant get LUID, we dont use the cache.
|
|
//
|
|
if ((err = BindCacheGetLuid(&Luid, &ModifiedId)) == NO_ERROR) {
|
|
|
|
//
|
|
// Try the cache for the passed in credentials.
|
|
//
|
|
|
|
if (pCacheEntry = BindCacheLookup(szDomainName, Luid, ModifiedId, Credentials, dwPort)) {
|
|
|
|
*ld = pCacheEntry ;
|
|
return S_OK ;
|
|
}
|
|
|
|
|
|
}
|
|
else {
|
|
|
|
//
|
|
// pick something that would NOT match anything else. and
|
|
// never put this in the cache
|
|
//
|
|
Luid = ReservedLuid ;
|
|
ModifiedId = ReservedLuid;
|
|
|
|
fAddToCache = FALSE ;
|
|
|
|
}
|
|
|
|
#else
|
|
//
|
|
// In the case of win95, however, we always make the Luid to be the reserved
|
|
// one and lookup the cache. If found use it, otherwise, add it.
|
|
//
|
|
|
|
Luid = ReservedLuid ;
|
|
ModifiedId = ReservedLuid;
|
|
|
|
if (pCacheEntry = BindCacheLookup(szDomainName, Luid, ModifiedId, Credentials, dwPort)) {
|
|
|
|
*ld = pCacheEntry ;
|
|
return S_OK ;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//
|
|
// allocate the pseudo handle (also the cache entry).
|
|
//
|
|
|
|
err = BindCacheAllocEntry(&pCacheEntry) ;
|
|
|
|
if (err != NO_ERROR) {
|
|
|
|
return HRESULT_FROM_WIN32(err);
|
|
}
|
|
|
|
|
|
if (!Credentials.IsNullCredentials()) {
|
|
|
|
hr = LdapOpenBindWithCredentials(
|
|
szDomainName,
|
|
szLDAPServer,
|
|
Credentials,
|
|
pCacheEntry,
|
|
dwPort
|
|
);
|
|
|
|
}else {
|
|
|
|
//
|
|
// We just want to try with NULL and NULL and
|
|
// whatever flags were passed in. No longer want
|
|
// to try and get credentials from registry.
|
|
//
|
|
|
|
|
|
hr = LdapOpenBindWithDefaultCredentials(
|
|
szDomainName,
|
|
szLDAPServer,
|
|
Credentials,
|
|
pCacheEntry,
|
|
dwPort
|
|
);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// This is the Server-Less case; where we retry with a force
|
|
// server to get a valid ServerName.
|
|
//
|
|
|
|
if (((hr == HRESULT_FROM_WIN32(ERROR_BAD_NETPATH)) ||
|
|
(hr == HRESULT_FROM_WIN32(ERROR_DS_SERVER_DOWN))) &&
|
|
!fVerify && fServerNotSpecified) {
|
|
fVerify = TRUE;
|
|
LdapUnbind(pCacheEntry);
|
|
goto RetryGetDefaultServer ;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// if success we add to cache. else unbind & cleanup.
|
|
//
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
if (!fAddToCache) {
|
|
|
|
//
|
|
// do not insert in cache since we didnt get LUID
|
|
//
|
|
|
|
}
|
|
else {
|
|
|
|
|
|
err = BindCacheAdd(szDomainName, Luid, ModifiedId, Credentials, dwPort, pCacheEntry) ;
|
|
|
|
if (err != NO_ERROR) {
|
|
|
|
LdapUnbind(pCacheEntry) ;
|
|
hr = HRESULT_FROM_WIN32(err);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Bind failed, force err so that we will clean up
|
|
// and return correct value
|
|
//
|
|
|
|
LdapUnbind(pCacheEntry) ; // needed to close out connection
|
|
//
|
|
// Set error value so that we do not return pCacheEntry.
|
|
//
|
|
err = ERROR_GEN_FAILURE;
|
|
|
|
}
|
|
|
|
if (err == NO_ERROR) {
|
|
|
|
*ld = pCacheEntry ;
|
|
}
|
|
else {
|
|
|
|
FreeADsMem(pCacheEntry) ;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
void LdapCloseObject(
|
|
ADS_LDP *ld
|
|
)
|
|
{
|
|
//
|
|
// We will delete only if the count is zero and the keeparound
|
|
// flag is not set.
|
|
//
|
|
if ((BindCacheDeref(ld) == 0) && !ld->fKeepAround) {
|
|
|
|
//
|
|
// ref count has dropped to zero and its gone from cache.
|
|
//
|
|
LdapUnbind(ld);
|
|
FreeADsMem(ld);
|
|
}
|
|
}
|
|
|
|
//
|
|
// This routine adds a ref to the pointer. Note that to release
|
|
// you use the LdapCloseObject routine.
|
|
//
|
|
void LdapCacheAddRef(
|
|
ADS_LDP *ld
|
|
)
|
|
{
|
|
ADsAssert(ld);
|
|
BindCacheAddRef(ld);
|
|
}
|
|
|
|
|
|
//
|
|
// NOTE: Ldap\ibute returns S_OK if attribute [szAttr] has no
|
|
// values (*[aValues]=NULL, *[ncount]=0) but all else ok.
|
|
//
|
|
|
|
HRESULT LdapReadAttribute(
|
|
WCHAR *szLDAPServer,
|
|
LPWSTR szLDAPDn,
|
|
WCHAR *szAttr,
|
|
WCHAR ***aValues,
|
|
int *nCount,
|
|
CCredentials& Credentials,
|
|
DWORD dwPort
|
|
)
|
|
{
|
|
return LdapReadAttribute2(
|
|
szLDAPServer,
|
|
NULL,
|
|
szLDAPDn,
|
|
szAttr,
|
|
aValues,
|
|
nCount,
|
|
Credentials,
|
|
dwPort
|
|
);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// NOTE: LdapReadAttribute2 returns S_OK if attribute [szAttr] has no
|
|
// values (*[aValues]=NULL, *[ncount]=0) but all else ok.
|
|
//
|
|
|
|
HRESULT LdapReadAttribute2(
|
|
WCHAR *szDomainName,
|
|
WCHAR *szLDAPServer,
|
|
LPWSTR szLDAPDn,
|
|
WCHAR *szAttr,
|
|
WCHAR ***aValues,
|
|
int *nCount,
|
|
CCredentials& Credentials,
|
|
DWORD dwPort,
|
|
LPWSTR szFilter // defaulted to NULL
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ADS_LDP *ld = NULL;
|
|
LPWSTR aStrings[2];
|
|
LDAPMessage *res = NULL;
|
|
LDAPMessage *e = NULL;
|
|
LPWSTR szFilterString = L"(objectClass=*)";
|
|
|
|
if (szFilter != NULL) {
|
|
szFilterString = szFilter;
|
|
}
|
|
|
|
hr = LdapOpenObject2(
|
|
szDomainName,
|
|
szLDAPServer,
|
|
szLDAPDn,
|
|
&ld,
|
|
Credentials,
|
|
dwPort
|
|
);
|
|
|
|
if (FAILED(hr))
|
|
goto CleanExit;
|
|
|
|
aStrings[0] = szAttr;
|
|
aStrings[1] = NULL;
|
|
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
hr = LdapSearchS( ld,
|
|
szLDAPDn,
|
|
LDAP_SCOPE_BASE,
|
|
szFilterString,
|
|
aStrings,
|
|
0,
|
|
&res );
|
|
|
|
// Only one entry should be returned
|
|
|
|
if ( FAILED(hr)
|
|
|| FAILED(hr = LdapFirstEntry( ld, res, &e ))
|
|
|| FAILED(hr = LdapGetValues( ld, e, szAttr, aValues, nCount ))
|
|
)
|
|
{
|
|
goto CleanExit;
|
|
}
|
|
|
|
CleanExit:
|
|
|
|
if ( res )
|
|
LdapMsgFree( res );
|
|
|
|
if ( ld ){
|
|
LdapCloseObject( ld );
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// This is similar to ReadAttribute but uses a handle to avoid
|
|
// the lookup in the bindCache
|
|
//
|
|
HRESULT LdapReadAttributeFast(
|
|
ADS_LDP *ld,
|
|
LPWSTR szLDAPDn,
|
|
WCHAR *szAttr,
|
|
WCHAR ***aValues,
|
|
int *nCount
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPWSTR aStrings[2];
|
|
LDAPMessage *res = NULL;
|
|
LDAPMessage *entry = NULL;
|
|
|
|
aStrings[0] = szAttr;
|
|
aStrings[1] = NULL;
|
|
|
|
hr = LdapSearchS(
|
|
ld,
|
|
szLDAPDn,
|
|
LDAP_SCOPE_BASE,
|
|
L"(objectClass=*)",
|
|
aStrings,
|
|
0,
|
|
&res
|
|
);
|
|
|
|
// Only one entry should be returned
|
|
|
|
if ( FAILED(hr)
|
|
|| FAILED(hr = LdapFirstEntry( ld, res, &entry ))
|
|
|| FAILED(hr = LdapGetValues( ld, entry, szAttr, aValues, nCount ))
|
|
)
|
|
{
|
|
goto CleanExit;
|
|
}
|
|
|
|
CleanExit:
|
|
|
|
if ( res )
|
|
LdapMsgFree( res );
|
|
|
|
RRETURN(hr);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Wrappers around the ldap functions
|
|
//
|
|
|
|
HRESULT LdapOpen( WCHAR *domainName, WCHAR *hostName, int portno, ADS_LDP *ld, DWORD dwFlags )
|
|
{
|
|
HRESULT hr = 0;
|
|
int ldaperr = 0;
|
|
PADS_LDP pCacheEntry = ld ;
|
|
DWORD dwOptions = 0;
|
|
|
|
void *ldapOption;
|
|
|
|
//
|
|
// Reset the handle first
|
|
//
|
|
pCacheEntry->LdapHandle = NULL;
|
|
|
|
if(dwFlags & ADS_USE_SSL)
|
|
pCacheEntry->LdapHandle = ldap_sslinit( domainName, portno, 1);
|
|
else
|
|
pCacheEntry->LdapHandle = ldap_init( domainName, portno );
|
|
|
|
if ( (pCacheEntry->LdapHandle) == NULL ) {
|
|
|
|
hr = HRESULT_FROM_WIN32(ERROR_BAD_NETPATH);
|
|
goto error;
|
|
}
|
|
|
|
ldaperr = ldap_get_option(pCacheEntry->LdapHandle, LDAP_OPT_GETDSNAME_FLAGS, &dwOptions);
|
|
|
|
if (ldaperr != LDAP_SUCCESS) {
|
|
CheckAndSetExtendedError(pCacheEntry->LdapHandle, &hr, ldaperr);
|
|
goto error;
|
|
}
|
|
|
|
if (portno == GC_PORT || portno == GC_SSL_PORT) {
|
|
|
|
dwOptions |= DS_GC_SERVER_REQUIRED;
|
|
|
|
} else if (!(dwFlags & ADS_READONLY_SERVER) ) {
|
|
|
|
dwOptions |= DS_WRITABLE_REQUIRED;
|
|
|
|
}
|
|
|
|
ldaperr = ldap_set_option(pCacheEntry->LdapHandle, LDAP_OPT_GETDSNAME_FLAGS,(void *)&dwOptions);
|
|
|
|
if (ldaperr != LDAP_SUCCESS) {
|
|
CheckAndSetExtendedError(pCacheEntry->LdapHandle, &hr, ldaperr);
|
|
goto error;
|
|
}
|
|
|
|
//
|
|
// Now process prompt for credentials
|
|
//
|
|
|
|
if (dwFlags & ADS_PROMPT_CREDENTIALS) {
|
|
|
|
ldapOption = (void *) LDAP_OPT_ON;
|
|
}else {
|
|
ldapOption = (void *) LDAP_OPT_OFF;
|
|
}
|
|
|
|
ldaperr = ldap_set_option(pCacheEntry->LdapHandle, LDAP_OPT_PROMPT_CREDENTIALS, &(ldapOption));
|
|
|
|
if (ldaperr != LDAP_SUCCESS) {
|
|
CheckAndSetExtendedError(pCacheEntry->LdapHandle, &hr, ldaperr);
|
|
goto error;
|
|
}
|
|
|
|
//
|
|
// Now init SSL if encryption is required. This is essentially if this option is on and the
|
|
// user did not specify an SSL port.
|
|
//
|
|
|
|
if (dwFlags & ADS_USE_ENCRYPTION) {
|
|
ldapOption = (void *) LDAP_OPT_ON;
|
|
ldaperr = ldap_set_option(pCacheEntry->LdapHandle, LDAP_OPT_SSL, &(ldapOption));
|
|
|
|
if (ldaperr != LDAP_SUCCESS) {
|
|
CheckAndSetExtendedError(pCacheEntry->LdapHandle, &hr, ldaperr);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Process the other options that can be set in the flags.
|
|
//
|
|
//
|
|
if (dwFlags & ADS_USE_SIGNING) {
|
|
//
|
|
// User has requested that packets be signed
|
|
//
|
|
ldapOption = (void *) LDAP_OPT_ON;
|
|
ldaperr = ldap_set_option(
|
|
pCacheEntry->LdapHandle,
|
|
LDAP_OPT_SIGN,
|
|
&(ldapOption)
|
|
);
|
|
|
|
if (ldaperr != LDAP_SUCCESS) {
|
|
CheckAndSetExtendedError(pCacheEntry->LdapHandle, &hr, ldaperr);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if (dwFlags & ADS_USE_SEALING) {
|
|
//
|
|
// User has requested that packet are sealed
|
|
//
|
|
ldapOption = (void *) LDAP_OPT_ON;
|
|
ldaperr = ldap_set_option(
|
|
pCacheEntry->LdapHandle,
|
|
LDAP_OPT_ENCRYPT,
|
|
&(ldapOption)
|
|
);
|
|
|
|
if (ldaperr != LDAP_SUCCESS) {
|
|
CheckAndSetExtendedError(pCacheEntry->LdapHandle, &hr, ldaperr);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now process versioning
|
|
//
|
|
|
|
ldapOption = (void *) LDAP_VERSION3;
|
|
|
|
ldaperr = ldap_set_option(
|
|
pCacheEntry->LdapHandle,
|
|
LDAP_OPT_VERSION,
|
|
&(ldapOption)
|
|
);
|
|
|
|
//
|
|
// Non critical if the above fails
|
|
//
|
|
|
|
if (hostName) {
|
|
ldapOption = (void *) hostName;
|
|
|
|
ldaperr = ldap_set_optionW(
|
|
pCacheEntry->LdapHandle,
|
|
LDAP_OPT_HOST_NAME,
|
|
&(ldapOption)
|
|
);
|
|
|
|
// Does not matter even if this setoption fails.
|
|
}
|
|
|
|
//
|
|
// Process delegation if set
|
|
//
|
|
if (dwFlags & ADS_USE_DELEGATION) {
|
|
|
|
#ifndef Win95
|
|
//
|
|
// Relying on LDAP/server to enforce the requirement for
|
|
// secure auth for this to work.
|
|
//
|
|
DWORD dwSSPIFlags = 0;
|
|
|
|
//
|
|
// Get the current values.
|
|
//
|
|
ldapOption = (void *)&dwSSPIFlags;
|
|
ldaperr = ldap_get_option(
|
|
pCacheEntry->LdapHandle,
|
|
LDAP_OPT_SSPI_FLAGS,
|
|
ldapOption
|
|
);
|
|
|
|
if (ldaperr != LDAP_SUCCESS) {
|
|
CheckAndSetExtendedError(pCacheEntry->LdapHandle, &hr, ldaperr);
|
|
goto error;
|
|
}
|
|
|
|
//
|
|
// Add delegation to the list.
|
|
//
|
|
dwSSPIFlags |= ISC_REQ_DELEGATE;
|
|
|
|
ldaperr = ldap_set_option(
|
|
pCacheEntry->LdapHandle,
|
|
LDAP_OPT_SSPI_FLAGS,
|
|
ldapOption
|
|
);
|
|
|
|
if (ldaperr != LDAP_SUCCESS) {
|
|
CheckAndSetExtendedError(pCacheEntry->LdapHandle, &hr, ldaperr);
|
|
goto error;
|
|
}
|
|
#else
|
|
BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER);
|
|
#endif
|
|
|
|
}
|
|
|
|
//
|
|
// Set the AREC exclusive option if applicable.
|
|
//
|
|
if (dwFlags & ADS_SERVER_BIND) {
|
|
|
|
ldapOption = (void *) LDAP_OPT_ON;
|
|
|
|
ldaperr = ldap_set_option(
|
|
pCacheEntry->LdapHandle,
|
|
LDAP_OPT_AREC_EXCLUSIVE,
|
|
&(ldapOption)
|
|
);
|
|
|
|
//
|
|
// Should we ignore this error ?
|
|
//
|
|
if (ldaperr) {
|
|
CheckAndSetExtendedError(pCacheEntry->LdapHandle, &hr, ldaperr);
|
|
goto error;
|
|
}
|
|
|
|
}
|
|
|
|
ldaperr = ldap_connect(pCacheEntry->LdapHandle, NULL);
|
|
|
|
if (ldaperr) {
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr );
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (pCacheEntry->LdapHandle) {
|
|
|
|
//
|
|
// Set the option for this connection giving our Callback functions
|
|
//
|
|
ldaperr = ldap_set_option(
|
|
pCacheEntry->LdapHandle,
|
|
LDAP_OPT_REFERRAL_CALLBACK,
|
|
&g_LdapReferralCallBacks
|
|
);
|
|
|
|
ldapOption = (void *) LDAP_CHASE_EXTERNAL_REFERRALS;
|
|
|
|
ldaperr = ldap_set_option(
|
|
pCacheEntry->LdapHandle,
|
|
LDAP_OPT_REFERRALS,
|
|
&(ldapOption)
|
|
);
|
|
|
|
}
|
|
|
|
return hr;
|
|
|
|
error:
|
|
|
|
if (pCacheEntry->LdapHandle != NULL) {
|
|
|
|
LdapUnbind(pCacheEntry);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT LdapBindS( ADS_LDP *ld, WCHAR *dn, WCHAR *passwd, BOOL fSimple )
|
|
{
|
|
LPWSTR pszNTLMUser = NULL;
|
|
LPWSTR pszNTLMDomain = NULL;
|
|
|
|
HRESULT hr = 0;
|
|
int ldaperr = 0;
|
|
DWORD dwLastLdapError = 0;
|
|
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
if (fSimple) {
|
|
|
|
ldaperr = ldap_simple_bind_s( ld->LdapHandle, dn, passwd ) ;
|
|
}
|
|
else {
|
|
|
|
if (dn || passwd) {
|
|
|
|
//
|
|
// If we have a non-null dn and/or a non-null password, we pass in
|
|
// a SEC_WINNT_AUTH_IDENTITY blob with the NTLM credentials
|
|
//
|
|
|
|
hr = LdapCrackUserDNtoNTLMUser2(
|
|
dn,
|
|
&pszNTLMUser,
|
|
&pszNTLMDomain
|
|
);
|
|
|
|
if (FAILED(hr)) {
|
|
hr = LdapCrackUserDNtoNTLMUser(
|
|
dn,
|
|
&pszNTLMUser,
|
|
&pszNTLMDomain
|
|
);
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
|
|
|
|
SEC_WINNT_AUTH_IDENTITY AuthI;
|
|
|
|
AuthI.User = (PWCHAR)pszNTLMUser;
|
|
AuthI.UserLength = (pszNTLMUser == NULL)? 0: wcslen(pszNTLMUser);
|
|
AuthI.Domain = (PWCHAR)pszNTLMDomain;
|
|
AuthI.DomainLength = (pszNTLMDomain == NULL)? 0: wcslen(pszNTLMDomain);
|
|
AuthI.Password = (PWCHAR)passwd;
|
|
AuthI.PasswordLength = (passwd == NULL)? 0: wcslen(passwd);
|
|
AuthI.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
|
|
|
|
|
|
|
|
|
|
ldaperr = ldap_bind_s( ld->LdapHandle, NULL, (WCHAR*)(&AuthI), LDAP_AUTH_SSPI );
|
|
|
|
}else {
|
|
|
|
//
|
|
// Otherwise we've come in with NULL, NULL - pass in NULL, NULL. The reason we
|
|
// do this is that ldap bind code oes not process NULL, NULL, NULL in the
|
|
// SEC_WINNT_AUTH_IDENTITY blob !!!
|
|
//
|
|
|
|
|
|
ldaperr = ldap_bind_s( ld->LdapHandle, NULL, NULL, LDAP_AUTH_SSPI );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr );
|
|
|
|
//
|
|
// If it is a local error for secure bind try and get
|
|
// more error info and store in ADsLastError.
|
|
//
|
|
if (!fSimple && (ldaperr == LDAP_LOCAL_ERROR)) {
|
|
|
|
dwLastLdapError = LdapGetLastError();
|
|
|
|
if (dwLastLdapError) {
|
|
//
|
|
// Set ADSI extended error code.
|
|
//
|
|
ADsSetLastError(
|
|
dwLastLdapError,
|
|
L"",
|
|
L"LDAP Provider"
|
|
);
|
|
}
|
|
}
|
|
|
|
error:
|
|
|
|
if (pszNTLMUser) {
|
|
|
|
FreeADsStr(pszNTLMUser);
|
|
}
|
|
|
|
if (pszNTLMDomain) {
|
|
|
|
FreeADsStr(pszNTLMDomain);
|
|
}
|
|
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT LdapUnbind( ADS_LDP *ld )
|
|
{
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
ADsAssert(ld);
|
|
|
|
if (ld->LdapHandle) {
|
|
|
|
int ldaperr = ldap_unbind( ld->LdapHandle );
|
|
// Need to set the handle to null as we may call
|
|
// unbind twice otherwise
|
|
|
|
ld->LdapHandle = NULL;
|
|
|
|
// There is nothing much we can do about err status
|
|
// CheckAndSetExtendedError could cause further problems
|
|
// if the handle is not valid so we return E_FAIL
|
|
// if the unbind failed.
|
|
|
|
if (ldaperr ) {
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT LdapSearchHelper(
|
|
LDAP *ld,
|
|
WCHAR *base,
|
|
int scope,
|
|
WCHAR *filter,
|
|
WCHAR *attrs[],
|
|
int attrsonly,
|
|
struct l_timeval *timeout,
|
|
LDAPMessage **res
|
|
)
|
|
{
|
|
HRESULT hr = 0;
|
|
int ldaperr = 0;
|
|
|
|
if ( timeout == NULL )
|
|
{
|
|
ldaperr = ldap_search_s( ld,
|
|
base,
|
|
scope,
|
|
filter,
|
|
attrs,
|
|
attrsonly,
|
|
res );
|
|
}
|
|
else
|
|
{
|
|
ldaperr = ldap_search_st( ld,
|
|
base,
|
|
scope,
|
|
filter,
|
|
attrs,
|
|
attrsonly,
|
|
timeout,
|
|
res );
|
|
}
|
|
|
|
if (ldaperr) {
|
|
|
|
if (!ldap_count_entries( ld, *res )) {
|
|
|
|
CheckAndSetExtendedError( ld, &hr, ldaperr, *res );
|
|
}
|
|
}else {
|
|
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT LdapSearchS(
|
|
ADS_LDP *ld,
|
|
WCHAR *base,
|
|
int scope,
|
|
WCHAR *filter,
|
|
WCHAR *attrs[],
|
|
int attrsonly,
|
|
LDAPMessage **res
|
|
)
|
|
{
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
HRESULT hr = LdapSearchHelper(
|
|
ld->LdapHandle,
|
|
base,
|
|
scope,
|
|
filter,
|
|
attrs,
|
|
attrsonly,
|
|
NULL,
|
|
res );
|
|
|
|
if ( LdapConnectionErr(hr, 0, NULL)) {
|
|
|
|
BindCacheInvalidateEntry(ld) ;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT LdapSearchST(
|
|
ADS_LDP *ld,
|
|
WCHAR *base,
|
|
int scope,
|
|
WCHAR *filter,
|
|
WCHAR *attrs[],
|
|
int attrsonly,
|
|
struct l_timeval *timeout,
|
|
LDAPMessage **res
|
|
)
|
|
{
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
HRESULT hr = LdapSearchHelper(
|
|
ld->LdapHandle,
|
|
base,
|
|
scope,
|
|
filter,
|
|
attrs,
|
|
attrsonly,
|
|
timeout,
|
|
res );
|
|
|
|
if ( LdapConnectionErr(hr, 0, NULL)) {
|
|
|
|
BindCacheInvalidateEntry(ld) ;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT LdapSearch(
|
|
ADS_LDP *ld,
|
|
WCHAR *base,
|
|
int scope,
|
|
WCHAR *filter,
|
|
WCHAR *attrs[],
|
|
int attrsonly,
|
|
int *msgid
|
|
)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
int ldaperr = 0;
|
|
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
*msgid = ldap_search( ld->LdapHandle,
|
|
base,
|
|
scope,
|
|
filter,
|
|
attrs,
|
|
attrsonly );
|
|
|
|
if ( *msgid == -1 )
|
|
{
|
|
ldaperr = LdapGetLastError();
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr);
|
|
}
|
|
|
|
if ( LdapConnectionErr(hr, 0, NULL)) {
|
|
|
|
BindCacheInvalidateEntry(ld) ;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT LdapModifyS(
|
|
ADS_LDP *ld,
|
|
WCHAR *dn,
|
|
LDAPModW *mods[]
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int ldaperr = 0;
|
|
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
ldaperr = ldap_modify_s( ld->LdapHandle, dn, mods);
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr );
|
|
|
|
if (LdapConnectionErr(hr, 0, NULL)) {
|
|
|
|
BindCacheInvalidateEntry(ld) ;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT LdapModifyExtS(
|
|
ADS_LDP *ld,
|
|
WCHAR *dn,
|
|
LDAPModW *mods[],
|
|
PLDAPControlW * ServerControls,
|
|
PLDAPControlW * ClientControls
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int ldaperr = 0;
|
|
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
ldaperr = ldap_modify_ext_s( ld->LdapHandle, dn, mods, ServerControls, ClientControls);
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr );
|
|
|
|
if (LdapConnectionErr(hr, 0, NULL)) {
|
|
|
|
BindCacheInvalidateEntry(ld) ;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT LdapModRdnS(
|
|
ADS_LDP *ld,
|
|
WCHAR *dn,
|
|
WCHAR *newrdn
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int ldaperr = 0;
|
|
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
ldaperr = ldap_modrdn_s( ld->LdapHandle, dn, newrdn );
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr);
|
|
|
|
if (LdapConnectionErr(hr, 0, NULL)) {
|
|
|
|
BindCacheInvalidateEntry(ld) ;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------------
|
|
//
|
|
// Function: LdapRenameExtS
|
|
//
|
|
// Synopsis: Extended renam/modifyRDN which will move objects across
|
|
// namespaces as opposed to modifyRDN which cannot do the same.
|
|
//
|
|
//
|
|
// Arguments: [ld] -- ldap handle.
|
|
// [dn] -- dn of object to be moved.
|
|
// [newRDN] -- New RDN of the object being moved.
|
|
// [newParent] -- New Parent.
|
|
// [deleteOldRDN] --
|
|
// [ServerControls]-- Server Control to be used.
|
|
// [ClientControls]-- Client Control to be used.
|
|
//-------------------------------------------------------------------------
|
|
HRESULT LdapRenameExtS(
|
|
ADS_LDP *ld,
|
|
WCHAR *dn,
|
|
WCHAR *newRDN,
|
|
WCHAR *newParent,
|
|
int deleteOldRDN,
|
|
PLDAPControlW * ServerControls,
|
|
PLDAPControlW * ClientControls
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int ldaperr = 0;
|
|
|
|
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
ldaperr = ldap_rename_ext_sW(
|
|
ld->LdapHandle,
|
|
dn,
|
|
newRDN,
|
|
newParent,
|
|
deleteOldRDN,
|
|
ServerControls,
|
|
ClientControls
|
|
);
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr);
|
|
|
|
if (LdapConnectionErr(hr, 0, NULL)) {
|
|
|
|
BindCacheInvalidateEntry(ld) ;
|
|
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT LdapModDnS(
|
|
ADS_LDP *ld,
|
|
WCHAR *dn,
|
|
WCHAR *newdn,
|
|
int deleteoldrdn
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int ldaperr = 0;
|
|
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
ldaperr = ldap_modrdn2_s(
|
|
ld->LdapHandle,
|
|
dn,
|
|
newdn,
|
|
deleteoldrdn );
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr);
|
|
|
|
if (LdapConnectionErr(hr, 0, NULL)) {
|
|
|
|
BindCacheInvalidateEntry(ld) ;
|
|
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT LdapAddS(
|
|
ADS_LDP *ld,
|
|
WCHAR *dn,
|
|
LDAPModW *attrs[]
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int ldaperr = 0;
|
|
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
ldaperr = ldap_add_s( ld->LdapHandle, dn, attrs );
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle , &hr, ldaperr);
|
|
|
|
if (LdapConnectionErr(hr, 0, NULL)) {
|
|
|
|
BindCacheInvalidateEntry(ld) ;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// Add ext wrapper function
|
|
//
|
|
HRESULT LdapAddExtS(
|
|
ADS_LDP *ld,
|
|
WCHAR *dn,
|
|
LDAPModW *attrs[],
|
|
PLDAPControl * ServerControls,
|
|
PLDAPControl * ClientControls
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int ldaperr = 0;
|
|
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
ldaperr = ldap_add_ext_s(
|
|
ld->LdapHandle,
|
|
dn,
|
|
attrs,
|
|
ServerControls,
|
|
ClientControls
|
|
);
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle , &hr, ldaperr);
|
|
|
|
if (LdapConnectionErr(hr, 0, NULL)) {
|
|
|
|
BindCacheInvalidateEntry(ld) ;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT LdapDeleteS(
|
|
ADS_LDP *ld,
|
|
WCHAR *dn
|
|
)
|
|
{
|
|
HRESULT hr = 0;
|
|
int ldaperr = 0;
|
|
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
ldaperr = ldap_delete_s( ld->LdapHandle, dn );
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr);
|
|
|
|
if (LdapConnectionErr(hr, 0, NULL)) {
|
|
|
|
BindCacheInvalidateEntry(ld) ;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT LdapDeleteExtS(
|
|
ADS_LDP *ld,
|
|
WCHAR *dn,
|
|
PLDAPControlW * ServerControls,
|
|
PLDAPControlW * ClientControls
|
|
)
|
|
{
|
|
HRESULT hr = 0;
|
|
int ldaperr = 0;
|
|
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
ldaperr = ldap_delete_ext_s(
|
|
ld->LdapHandle,
|
|
dn,
|
|
ServerControls,
|
|
ClientControls
|
|
);
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr);
|
|
|
|
if (LdapConnectionErr(hr, 0, NULL)) {
|
|
|
|
BindCacheInvalidateEntry(ld) ;
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
HRESULT
|
|
LdapCompareExt(
|
|
ADS_LDP *ld,
|
|
const LPWSTR pszDn,
|
|
const LPWSTR pszAttribute,
|
|
const LPWSTR pszValue,
|
|
struct berval *berData,
|
|
PLDAPControlW * ServerControls,
|
|
PLDAPControlW * ClientControls
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int ldaperr = 0;
|
|
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
ldaperr = ldap_compare_ext_s(
|
|
ld->LdapHandle,
|
|
pszDn,
|
|
pszAttribute,
|
|
pszValue,
|
|
berData,
|
|
ServerControls,
|
|
ClientControls
|
|
);
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr);
|
|
|
|
if (LdapConnectionErr(hr, 0, NULL)) {
|
|
BindCacheInvalidateEntry(ld) ;
|
|
}
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
|
|
HRESULT LdapAbandon(
|
|
ADS_LDP *ld,
|
|
int msgid
|
|
)
|
|
{
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
// No error code, 0 if success, -1 otherwise
|
|
return ldap_abandon( ld->LdapHandle, msgid );
|
|
}
|
|
|
|
HRESULT LdapResult(
|
|
ADS_LDP *ld,
|
|
int msgid,
|
|
int all,
|
|
struct l_timeval *timeout,
|
|
LDAPMessage **res,
|
|
int *restype
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int ldaperr = 0;
|
|
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
*restype = ldap_result( ld->LdapHandle, msgid, all, timeout, res );
|
|
|
|
if ( *restype == -1 ) // error
|
|
ldaperr = LdapGetLastError();
|
|
|
|
if (ldaperr) {
|
|
|
|
if (!ldap_count_entries( ld->LdapHandle, *res )) {
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr, *res );
|
|
|
|
if ( LdapConnectionErr(hr, 0, NULL)) {
|
|
|
|
BindCacheInvalidateEntry(ld) ;
|
|
}
|
|
}
|
|
}else {
|
|
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
void LdapMsgFree(
|
|
LDAPMessage *res
|
|
)
|
|
{
|
|
ldap_msgfree( res ); // Returns the type of message freed which
|
|
// is not interesting
|
|
}
|
|
|
|
int LdapResult2Error(
|
|
ADS_LDP *ld,
|
|
LDAPMessage *res,
|
|
int freeit
|
|
)
|
|
{
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
return ldap_result2error( ld->LdapHandle, res, freeit );
|
|
}
|
|
|
|
HRESULT LdapFirstEntry(
|
|
ADS_LDP *ld,
|
|
LDAPMessage *res,
|
|
LDAPMessage **pfirst
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int ldaperr = 0;
|
|
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
*pfirst = ldap_first_entry( ld->LdapHandle, res );
|
|
|
|
if ( *pfirst == NULL )
|
|
{
|
|
ldaperr = LdapGetLastError();
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr, res);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT LdapNextEntry(
|
|
ADS_LDP *ld,
|
|
LDAPMessage *entry,
|
|
LDAPMessage **pnext
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int ldaperr = 0;
|
|
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
*pnext = ldap_next_entry( ld->LdapHandle, entry );
|
|
|
|
if ( *pnext == NULL )
|
|
{
|
|
ldaperr = LdapGetLastError();
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr, entry);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
int LdapCountEntries(
|
|
ADS_LDP *ld,
|
|
LDAPMessage *res
|
|
)
|
|
{
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
return ldap_count_entries( ld->LdapHandle, res );
|
|
}
|
|
|
|
HRESULT LdapFirstAttribute(
|
|
ADS_LDP *ld,
|
|
LDAPMessage *entry,
|
|
void **ptr,
|
|
WCHAR **pattr
|
|
)
|
|
{
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
LPWSTR pAttrTemp = NULL;
|
|
|
|
// NOTE: The return value from ldap_first_attribute is static and
|
|
// should not be freed
|
|
|
|
pAttrTemp = ldap_first_attribute( ld->LdapHandle, entry,
|
|
(struct berelement **) ptr ); // static data
|
|
|
|
if ( pAttrTemp == NULL )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int ldaperr = 0;
|
|
|
|
// Error occurred or end of attributes
|
|
|
|
ldaperr = LdapGetLastError();
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr, entry);
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Copy over the value as we can loose state if this fn is
|
|
// called again - for example we read the attribute and are
|
|
// now looking for schema information about it and end up
|
|
// fetching the schema from the server.
|
|
//
|
|
*pattr = AllocADsStr(pAttrTemp);
|
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
HRESULT LdapNextAttribute(
|
|
ADS_LDP *ld,
|
|
LDAPMessage *entry,
|
|
void *ptr,
|
|
WCHAR **pattr
|
|
)
|
|
{
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
LPWSTR pAttrTemp = NULL;
|
|
|
|
// NOTE: The return value from ldap_next_attribute is static and
|
|
// should not be freed
|
|
pAttrTemp = ldap_next_attribute( ld->LdapHandle, entry,
|
|
(struct berelement *) ptr ); // static data
|
|
|
|
if (pAttrTemp) {
|
|
*pattr = AllocADsStr(pAttrTemp);
|
|
} else {
|
|
*pattr = NULL;
|
|
}
|
|
|
|
#if 0 // Ignore the error code here since at the end of the enumeration,
|
|
// we will probably get an error code here ( both Andy and umich's
|
|
// dll will return errors sometimes. No error returned from NTDS,
|
|
// but errors are returned from Exchange server )
|
|
|
|
if ( *pattr == NULL )
|
|
{
|
|
HRESULT hr = NO_ERROR;
|
|
int ldaperr = 0;
|
|
|
|
// Error occurred or end of attributes
|
|
ldaperr = LdapGetLastError();
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr);
|
|
return hr;
|
|
}
|
|
#endif
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//
|
|
// NOTE: LdapGetValues return S_OK if attribute [attr] has no values
|
|
// (*[pvalues] =NULL, *[pcount]=0) but all else ok.
|
|
//
|
|
|
|
HRESULT LdapGetValues(
|
|
ADS_LDP *ld,
|
|
LDAPMessage *entry,
|
|
WCHAR *attr,
|
|
WCHAR ***pvalues,
|
|
int *pcount
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int ldaperr = 0;
|
|
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
*pvalues = ldap_get_values( ld->LdapHandle, entry, attr );
|
|
|
|
if ( *pvalues == NULL ) {
|
|
|
|
*pcount=0;
|
|
|
|
//
|
|
// ldap_get_values succeeds if attribute has no values
|
|
// but all else ok. (confiremed with anoopa)
|
|
//
|
|
|
|
ldaperr = LdapGetLastError();
|
|
|
|
if (ldaperr) {
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr, entry);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
*pcount = ldap_count_values( *pvalues );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//
|
|
// NOTE: LdapGetValuesLen return S_OK if attribute [attr] has no values
|
|
// (*[pvalues] =NULL, *[pcount]=0) but all else ok.
|
|
//
|
|
|
|
HRESULT LdapGetValuesLen(
|
|
ADS_LDP *ld,
|
|
LDAPMessage *entry,
|
|
WCHAR *attr,
|
|
struct berval ***pvalues,
|
|
int *pcount
|
|
)
|
|
{
|
|
//
|
|
// NOTE: this can contain binary data as well as strings,
|
|
// strings are ascii, no conversion is done here
|
|
//
|
|
|
|
HRESULT hr = S_OK;
|
|
int ldaperr = 0;
|
|
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
*pvalues = ldap_get_values_len( ld->LdapHandle, entry, attr );
|
|
|
|
if ( *pvalues == NULL ){
|
|
|
|
*pcount=0;
|
|
|
|
//
|
|
// ldap_get_values succeeds if attribute has no values
|
|
// but all else ok. (confiremed with anoopa)
|
|
//
|
|
|
|
ldaperr = LdapGetLastError();
|
|
|
|
if (ldaperr) {
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr, entry);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
*pcount = ldap_count_values_len( *pvalues );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
void LdapValueFree(
|
|
WCHAR **vals
|
|
)
|
|
{
|
|
ldap_value_free( vals );
|
|
}
|
|
|
|
void LdapValueFreeLen(
|
|
struct berval **vals
|
|
)
|
|
{
|
|
ldap_value_free_len( vals );
|
|
}
|
|
|
|
void LdapMemFree(
|
|
WCHAR *pszString
|
|
)
|
|
{
|
|
ldap_memfree( pszString );
|
|
}
|
|
|
|
void LdapAttributeFree(
|
|
WCHAR *pszString
|
|
)
|
|
{
|
|
//
|
|
// Since we made a copy of the string returned by LDAP, we
|
|
// need to free it here.
|
|
//
|
|
|
|
FreeADsStr(pszString);
|
|
}
|
|
|
|
HRESULT LdapGetDn(
|
|
ADS_LDP *ld,
|
|
LDAPMessage *entry,
|
|
WCHAR **pdn
|
|
)
|
|
{
|
|
int ldaperr = 0;
|
|
HRESULT hr = S_OK;
|
|
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
*pdn = ldap_get_dn( ld->LdapHandle, entry );
|
|
if ( *pdn == NULL )
|
|
{
|
|
// Error occurred
|
|
ldaperr = LdapGetLastError();
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr, entry);
|
|
return hr;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
int ConvertToAscii( WCHAR *pszUnicode, char **pszAscii )
|
|
{
|
|
|
|
int nSize;
|
|
|
|
if ( pszUnicode == NULL )
|
|
{
|
|
*pszAscii = NULL;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
nSize = WideCharToMultiByte( CP_ACP,
|
|
0,
|
|
pszUnicode,
|
|
-1,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL );
|
|
|
|
if ( nSize == 0 )
|
|
return GetLastError();
|
|
|
|
*pszAscii = (char * ) AllocADsMem( nSize );
|
|
|
|
if ( *pszAscii == NULL )
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
if ( !WideCharToMultiByte( CP_ACP,
|
|
0,
|
|
pszUnicode,
|
|
-1,
|
|
*pszAscii,
|
|
nSize,
|
|
NULL,
|
|
NULL ))
|
|
{
|
|
FreeADsMem( *pszAscii );
|
|
*pszAscii = NULL;
|
|
return GetLastError();
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
int ConvertToUnicode( char *pszAscii, WCHAR **pszUnicode )
|
|
{
|
|
int nSize;
|
|
|
|
*pszUnicode = NULL;
|
|
|
|
if ( pszAscii == NULL )
|
|
{
|
|
return NO_ERROR;
|
|
}
|
|
|
|
nSize = MultiByteToWideChar( CP_ACP,
|
|
0,
|
|
pszAscii,
|
|
-1,
|
|
NULL,
|
|
0 );
|
|
|
|
if ( nSize == 0 )
|
|
return GetLastError();
|
|
|
|
*pszUnicode = (WCHAR * ) AllocADsMem( nSize * sizeof(WCHAR));
|
|
|
|
if ( *pszUnicode == NULL )
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
if ( !MultiByteToWideChar( CP_ACP,
|
|
0,
|
|
pszAscii,
|
|
-1,
|
|
*pszUnicode,
|
|
nSize ))
|
|
{
|
|
FreeADsMem( *pszUnicode );
|
|
*pszUnicode = NULL;
|
|
return GetLastError();
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
// Dead function?
|
|
int ConvertLDAPMod( LDAPModW **mods, LDAPModA ***modsA )
|
|
{
|
|
int nCount = 0;
|
|
LDAPModA *aMods = NULL;
|
|
int err = NO_ERROR;
|
|
|
|
if ( mods == NULL )
|
|
{
|
|
*modsA = NULL;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
while ( mods[nCount] )
|
|
nCount++;
|
|
|
|
*modsA = (LDAPModA **) AllocADsMem((nCount+1) * sizeof(LDAPModA *));
|
|
|
|
if ( *modsA == NULL )
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
aMods = (LDAPModA *) AllocADsMem(nCount * sizeof(LDAPModA));
|
|
|
|
if ( aMods == NULL )
|
|
{
|
|
FreeADsMem( *modsA );
|
|
*modsA = NULL;
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
for ( int i = 0; i < nCount; i++ )
|
|
{
|
|
aMods[i].mod_op = mods[i]->mod_op;
|
|
|
|
if ( err = ConvertToAscii( mods[i]->mod_type, &(aMods[i].mod_type)))
|
|
break;
|
|
|
|
if ( aMods[i].mod_op & LDAP_MOD_BVALUES )
|
|
{
|
|
aMods[i].mod_bvalues = mods[i]->mod_bvalues;
|
|
}
|
|
else
|
|
{
|
|
int nStrCount = 0;
|
|
WCHAR **aStrings = mods[i]->mod_values;
|
|
|
|
while ( aStrings[nStrCount] )
|
|
nStrCount++;
|
|
|
|
aMods[i].mod_values = (char **) AllocADsMem(
|
|
(nStrCount+1) * sizeof(char *));
|
|
|
|
if ( aMods[i].mod_values != NULL )
|
|
{
|
|
for ( int j = 0; j < nStrCount; j++ )
|
|
{
|
|
err = ConvertToAscii( mods[i]->mod_values[j],
|
|
&(aMods[i].mod_values[j]));
|
|
if ( err )
|
|
{
|
|
for ( int k = 0; k < j; k++ )
|
|
FreeADsMem( aMods[i].mod_values[k] );
|
|
FreeADsMem( aMods[i].mod_values );
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
if ( err )
|
|
break;
|
|
}
|
|
|
|
(*modsA)[i] = &aMods[i];
|
|
}
|
|
|
|
if ( err )
|
|
{
|
|
for ( int k = 0; k < i; k++ )
|
|
{
|
|
FreeADsMem( aMods[k].mod_type );
|
|
|
|
if ( !(aMods[k].mod_op & LDAP_MOD_BVALUES ))
|
|
{
|
|
int j = 0;
|
|
while ( aMods[k].mod_values[j] )
|
|
FreeADsMem( aMods[k].mod_values[j++] );
|
|
|
|
if ( aMods[k].mod_values )
|
|
FreeADsMem( aMods[k].mod_values );
|
|
}
|
|
}
|
|
|
|
FreeADsMem( aMods );
|
|
FreeADsMem( *modsA );
|
|
*modsA = NULL;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
// Dead function?
|
|
void FreeLDAPMod( LDAPModA **modsA )
|
|
{
|
|
int i = 0;
|
|
while ( modsA[i] )
|
|
{
|
|
FreeADsMem( modsA[i]->mod_type );
|
|
|
|
if ( !(modsA[i]->mod_op & LDAP_MOD_BVALUES ))
|
|
{
|
|
int j = 0;
|
|
while ( modsA[i]->mod_values[j] )
|
|
FreeADsMem( modsA[i]->mod_values[j++] );
|
|
|
|
if ( modsA[i]->mod_values )
|
|
FreeADsMem( modsA[i]->mod_values );
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
if ( modsA[0] )
|
|
FreeADsMem( modsA[0] );
|
|
|
|
FreeADsMem( modsA );
|
|
}
|
|
|
|
|
|
void
|
|
LdapGetCredentialsFromRegistry(
|
|
CCredentials& Credentials
|
|
)
|
|
{
|
|
|
|
HKEY hKey ;
|
|
WCHAR UserName[128], Password[128] ;
|
|
LPWSTR pUserName = NULL;
|
|
LPWSTR pPassword = NULL;
|
|
DWORD dwType, cbUserName = sizeof(UserName),
|
|
cbPassword = sizeof(Password);
|
|
int err = NO_ERROR;
|
|
|
|
|
|
UserName[0] = Password[0] = 0 ;
|
|
|
|
//
|
|
// Open the registry Key
|
|
//
|
|
|
|
err = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
|
|
L"System\\CurrentControlSet\\Services\\TcpIp\\Parameters",
|
|
0L,
|
|
KEY_READ,
|
|
&hKey) ;
|
|
|
|
if (err == NO_ERROR) {
|
|
|
|
err = RegQueryValueEx (hKey,
|
|
L"LdapUserName",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE) UserName,
|
|
&cbUserName) ;
|
|
|
|
if (err == NO_ERROR) {
|
|
|
|
pUserName = UserName;
|
|
}
|
|
|
|
err = RegQueryValueEx (hKey,
|
|
L"LdapPassword",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE) Password,
|
|
&cbPassword) ;
|
|
|
|
if (err == NO_ERROR) {
|
|
|
|
pPassword = Password;
|
|
}
|
|
|
|
Credentials.SetUserName(pUserName);
|
|
|
|
Credentials.SetPassword(pPassword);
|
|
|
|
RegCloseKey(hKey) ;
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT
|
|
LdapOpenBindWithCredentials(
|
|
WCHAR *szDomainName,
|
|
WCHAR *szServerName,
|
|
CCredentials& Credentials,
|
|
PADS_LDP pCacheEntry,
|
|
DWORD dwPort
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPWSTR UserName = NULL;
|
|
LPWSTR Password = NULL;
|
|
DWORD dwFlagsChecked = 0;
|
|
DWORD dwAuthFlags = 0;
|
|
|
|
ULONG ulSSLPort, ulPort;
|
|
|
|
hr = Credentials.GetUserName(&UserName);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
|
|
hr = Credentials.GetPassword(&Password);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
//
|
|
// dwAuthFlags are the acutal flags set by the user
|
|
//
|
|
dwAuthFlags = Credentials.GetAuthFlags();
|
|
|
|
//
|
|
// dwFlagsChecked are the flags that we handle in this fn.
|
|
//
|
|
dwFlagsChecked = MaskKnownFlagsNotChecked(dwAuthFlags);
|
|
|
|
if (dwPort == USE_DEFAULT_LDAP_PORT) {
|
|
//
|
|
// We can choose the appropriate LDAP port no.
|
|
//
|
|
ulSSLPort = LDAP_SSL_PORT;
|
|
ulPort = LDAP_PORT;
|
|
}
|
|
else if ( dwPort == USE_DEFAULT_GC_PORT ) {
|
|
//
|
|
// We can choose the appropriate GC port no.
|
|
//
|
|
ulSSLPort = GC_SSL_PORT;
|
|
ulPort = GC_PORT;
|
|
}
|
|
else {
|
|
//
|
|
// Port number explicitly specified; go with that
|
|
//
|
|
ulSSLPort = dwPort;
|
|
ulPort = dwPort;
|
|
}
|
|
|
|
switch (dwFlagsChecked) {
|
|
|
|
|
|
case (ADS_USE_ENCRYPTION | ADS_SECURE_AUTHENTICATION):
|
|
|
|
//
|
|
// Case 1 - ADS_USE_ENCRYPTION AND ADS_SECURE_AUTHENTICATION
|
|
// We do a negotiate bind over ssl channel
|
|
//
|
|
|
|
hr = LdapOpen( szDomainName, szServerName, ulSSLPort, pCacheEntry, dwAuthFlags );
|
|
if (FAILED(hr)) {
|
|
goto error;
|
|
}
|
|
|
|
hr = LdapBindS( pCacheEntry, UserName, Password, FALSE );
|
|
|
|
if (FAILED(hr) && LDAPCodeWarrantsRetry(hr)) {
|
|
|
|
//
|
|
// Try setting the version number to V2.
|
|
|
|
void *ldapOption = (void *) LDAP_VERSION2;
|
|
|
|
ldap_set_option(
|
|
pCacheEntry->LdapHandle,
|
|
LDAP_OPT_VERSION,
|
|
&(ldapOption)
|
|
);
|
|
|
|
hr = LdapBindS( pCacheEntry, UserName, Password, FALSE );
|
|
|
|
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
}
|
|
break;
|
|
|
|
|
|
case (ADS_SECURE_AUTHENTICATION):
|
|
|
|
//
|
|
// Case 2 - ADS_SECURE_AUTHENTICATION
|
|
// we will try opening the LDAP port and authenticate with
|
|
// SSPI.
|
|
//
|
|
|
|
hr = LdapOpen( szDomainName, szServerName, ulPort, pCacheEntry, dwAuthFlags);
|
|
if (FAILED(hr)) {
|
|
|
|
goto error;
|
|
}
|
|
|
|
hr = LdapBindS( pCacheEntry, UserName, Password, FALSE );
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
break;
|
|
|
|
case (ADS_USE_ENCRYPTION):
|
|
|
|
//
|
|
// Case 3 - ADS_USE_ENCRYPTION
|
|
// We will first try open the SSL port. If this succeeds, we
|
|
// will try a simple bind. If the open to the SSL port fails,
|
|
// we will error out - (we can't provide encryption)
|
|
//
|
|
|
|
|
|
hr = LdapOpen( szDomainName, szServerName, ulSSLPort, pCacheEntry, dwAuthFlags );
|
|
if (FAILED(hr)) {
|
|
goto error;
|
|
}
|
|
|
|
hr = LdapBindS( pCacheEntry, UserName, Password, TRUE );
|
|
|
|
if (FAILED(hr) && LDAPCodeWarrantsRetry(hr)) {
|
|
|
|
//
|
|
// Try setting the version number to V2.
|
|
|
|
void *ldapOption = (void *) LDAP_VERSION2;
|
|
|
|
ldap_set_option(
|
|
pCacheEntry->LdapHandle,
|
|
LDAP_OPT_VERSION,
|
|
&(ldapOption)
|
|
);
|
|
|
|
hr = LdapBindS( pCacheEntry, UserName, Password, TRUE );
|
|
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case 0:
|
|
|
|
//
|
|
// Case 4 - NOT ADS_SECURE_AUTHENTICATION AND NOT ADS_USE_ENCRYPTION
|
|
|
|
// We will first try opening the LDAP port. Then we will do
|
|
// a simple bind and "clear-text" authenticate.
|
|
//
|
|
|
|
hr = LdapOpen( szDomainName, szServerName, ulPort, pCacheEntry, dwAuthFlags );
|
|
if (FAILED(hr)) {
|
|
goto error;
|
|
}
|
|
|
|
//
|
|
// Note there is no need to try an SSPI bind, as the user came with
|
|
// explicit credentials and requested no authentication / no encryption
|
|
// We just need to do a simple bind.
|
|
|
|
// err = LdapBindS( pCacheEntry, UserName, Password, FALSE );
|
|
hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED);
|
|
if (FAILED(hr)) {
|
|
|
|
//
|
|
// SSPI bind failed - try a simple bind.
|
|
//
|
|
|
|
hr = LdapBindS( pCacheEntry, UserName, Password, TRUE );
|
|
if (FAILED(hr) && LDAPCodeWarrantsRetry(hr)) {
|
|
|
|
//
|
|
// Try setting the version number to V2.
|
|
|
|
void *ldapOption = (void *) LDAP_VERSION2;
|
|
|
|
ldap_set_option(
|
|
pCacheEntry->LdapHandle,
|
|
LDAP_OPT_VERSION,
|
|
&(ldapOption)
|
|
);
|
|
|
|
hr = LdapBindS( pCacheEntry, UserName, Password, TRUE );
|
|
|
|
if (FAILED(hr)) {
|
|
goto error;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case ADS_NO_AUTHENTICATION:
|
|
|
|
//
|
|
// Case 10 - ADS_NO_AUTHENTICATION
|
|
//
|
|
hr = LdapOpen( szDomainName, szServerName, ulPort, pCacheEntry, dwAuthFlags);
|
|
if (FAILED(hr)) {
|
|
goto error;
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
//
|
|
// Case 5 - Bogus values - if we come in with any bogus flags, we
|
|
// will fail with ERROR_INVALID_PARAMETER.
|
|
//
|
|
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
error:
|
|
|
|
if (UserName) {
|
|
FreeADsStr(UserName);
|
|
}
|
|
|
|
if (Password) {
|
|
SecureZeroMemory(Password, wcslen(Password)*sizeof(WCHAR));
|
|
FreeADsStr(Password);
|
|
}
|
|
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
LdapOpenBindWithDefaultCredentials(
|
|
WCHAR *szDomainName,
|
|
WCHAR *szServerName,
|
|
CCredentials& Credentials,
|
|
PADS_LDP pCacheEntry,
|
|
DWORD dwPort
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPWSTR UserName = NULL;
|
|
LPWSTR Password = NULL;
|
|
DWORD dwFlagsChecked = 0;
|
|
DWORD dwAuthFlags = 0;
|
|
|
|
ULONG ulSSLPort, ulPort;
|
|
|
|
|
|
hr = Credentials.GetUserName(&UserName);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
hr = Credentials.GetPassword(&Password);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
//
|
|
// These are the flags actually passed in.
|
|
//
|
|
dwAuthFlags = Credentials.GetAuthFlags();
|
|
|
|
//
|
|
// dwFlagsChecked contains only those flags we check for here.
|
|
//
|
|
dwFlagsChecked = MaskKnownFlagsNotChecked(dwAuthFlags);
|
|
|
|
if (dwPort == USE_DEFAULT_LDAP_PORT) {
|
|
//
|
|
// We can choose the LDAP port number
|
|
//
|
|
ulSSLPort = LDAP_SSL_PORT;
|
|
ulPort = LDAP_PORT;
|
|
}
|
|
else if ( dwPort == USE_DEFAULT_GC_PORT ) {
|
|
//
|
|
// We can choose the appropriate GC port number
|
|
//
|
|
ulSSLPort = GC_SSL_PORT;
|
|
ulPort = GC_PORT;
|
|
}
|
|
else {
|
|
//
|
|
// Port number explicitly specified; go with that
|
|
//
|
|
ulSSLPort = dwPort;
|
|
ulPort = dwPort;
|
|
}
|
|
|
|
switch (dwFlagsChecked) {
|
|
|
|
case (ADS_USE_ENCRYPTION | ADS_SECURE_AUTHENTICATION):
|
|
|
|
|
|
//
|
|
// Case 1 - ADS_USE_ENCRYPTION AND ADS_SECURE_AUTHENTICATION
|
|
// We do negotiate bind over SSL channel
|
|
//
|
|
|
|
hr = LdapOpen( szDomainName, szServerName, ulSSLPort, pCacheEntry, dwAuthFlags );
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
hr = LdapBindS( pCacheEntry, UserName, Password, FALSE );
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
//
|
|
// Try setting the version number to V2.
|
|
|
|
void *ldapOption = (void *) LDAP_VERSION2;
|
|
|
|
ldap_set_option(
|
|
pCacheEntry->LdapHandle,
|
|
LDAP_OPT_VERSION,
|
|
&(ldapOption)
|
|
);
|
|
|
|
hr = LdapBindS( pCacheEntry, UserName, Password, FALSE );
|
|
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
}
|
|
break;
|
|
|
|
|
|
case (ADS_SECURE_AUTHENTICATION):
|
|
|
|
//
|
|
// Case 2 - ADS_SECURE_AUTHENTICATION
|
|
// we will try opening the LDAP port and authenticate with
|
|
// SSPI.
|
|
//
|
|
|
|
hr = LdapOpen( szDomainName, szServerName, ulPort, pCacheEntry, dwAuthFlags);
|
|
if (FAILED(hr)) {
|
|
|
|
goto error;
|
|
}
|
|
|
|
hr = LdapBindS( pCacheEntry, UserName, Password, FALSE );
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
break;
|
|
|
|
case (ADS_USE_ENCRYPTION):
|
|
|
|
//
|
|
// Case 3 - ADS_USE_ENCRYPTION
|
|
// We will first try open the SSL port. If this succeeds, we
|
|
// will try a simple bind. If the open to the SSL port fails,
|
|
// we will error out - (we can't provide encryption)
|
|
//
|
|
|
|
hr = LdapOpen( szDomainName, szServerName, ulSSLPort, pCacheEntry, dwAuthFlags );
|
|
if (FAILED(hr)) {
|
|
goto error;
|
|
}
|
|
|
|
hr = LdapBindS( pCacheEntry, UserName, Password, TRUE );
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
//
|
|
// Try setting the version number to V2.
|
|
|
|
void *ldapOption = (void *) LDAP_VERSION2;
|
|
|
|
ldap_set_option(
|
|
pCacheEntry->LdapHandle,
|
|
LDAP_OPT_VERSION,
|
|
&(ldapOption)
|
|
);
|
|
|
|
hr = LdapBindS( pCacheEntry, UserName, Password, TRUE );
|
|
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
//
|
|
// Case 4 - NOT ADS_SECURE_AUTHENTICATION AND NOT ADS_USE_ENCRYPTION
|
|
|
|
// We will first try opening the LDAP port. If this succeeds, we
|
|
// will try an SSPI bind. We still want to try and provide the user
|
|
// with a secure authentication even though he/she is crazy enough
|
|
// to unsecurely authenticate. If the SSPI bind fails, we will do
|
|
// a simple bind and "clear-text" authenticate.
|
|
//
|
|
|
|
hr = LdapOpen( szDomainName, szServerName, ulPort, pCacheEntry, dwAuthFlags );
|
|
if (FAILED(hr)) {
|
|
goto error;
|
|
}
|
|
|
|
hr = LdapBindS( pCacheEntry, UserName, Password, FALSE );
|
|
if (FAILED(hr) && hr != ERROR_LOGON_FAILURE) {
|
|
|
|
hr = LdapBindS( pCacheEntry, UserName, Password, TRUE );
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
//
|
|
// Try setting the version number to V2.
|
|
//
|
|
void *ldapOption = (void *) LDAP_VERSION2;
|
|
|
|
ldap_set_option(
|
|
pCacheEntry->LdapHandle,
|
|
LDAP_OPT_VERSION,
|
|
&(ldapOption)
|
|
);
|
|
|
|
hr = LdapBindS( pCacheEntry, UserName, Password, TRUE );
|
|
|
|
if (FAILED(hr)) {
|
|
goto error;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
//
|
|
// Case 5 - Bogus values - if we come in with any bogus flags, we
|
|
// will fail with ERROR_INVALID_PARAMETER.
|
|
//
|
|
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
error:
|
|
|
|
if (UserName) {
|
|
FreeADsStr(UserName);
|
|
}
|
|
|
|
if (Password) {
|
|
FreeADsStr(Password);
|
|
}
|
|
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// Function: LdapCrackUserDNToNTLMUser
|
|
//
|
|
// Synopsis: This call attempts to translate an LDAP-style DN into an NTLM
|
|
// domain password form. The algorithm is to assume that the first
|
|
// component will be of the form "CN=krishnaG" . This first component
|
|
// will be taken as the user name. The subsequent components are
|
|
// scanned till we find the first DC= component.
|
|
//
|
|
// Arguments: [LPTSTR szBuffer]
|
|
// [LPVOID *ppObject]
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
// Modifies: -
|
|
//
|
|
// History: 11-3-95 krishnag Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
LdapCrackUserDNtoNTLMUser(
|
|
LPWSTR pszDN,
|
|
LPWSTR * ppszNTLMUser,
|
|
LPWSTR * ppszNTLMDomain
|
|
)
|
|
{
|
|
OBJECTINFO ObjectInfo;
|
|
POBJECTINFO pObjectInfo = &ObjectInfo;
|
|
WCHAR szDomainName[MAX_PATH];
|
|
LPWSTR szToken = NULL;
|
|
|
|
LPWSTR pszSrcComp = NULL;
|
|
LPWSTR pszSrcValue = NULL;
|
|
|
|
DWORD i = 0;
|
|
DWORD dwNumComponents = 0;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
CLexer Lexer;
|
|
hr = Lexer.InitializePath(pszDN);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
CLexer * pTokenizer = &Lexer;
|
|
|
|
DWORD dwToken = 0;
|
|
|
|
LPWSTR pszUserName = NULL;
|
|
LPWSTR pszDomainName = NULL;
|
|
|
|
|
|
*ppszNTLMUser = NULL;
|
|
*ppszNTLMDomain = NULL;
|
|
|
|
if (pszDN && !*pszDN) {
|
|
//
|
|
// Empty string.
|
|
//
|
|
*ppszNTLMUser = AllocADsStr(L"");
|
|
|
|
if (!*ppszNTLMUser) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
*ppszNTLMDomain = AllocADsStr(L"");
|
|
|
|
if (!*ppszNTLMDomain) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
hr = InitObjectInfo(pszDN, pObjectInfo);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
szDomainName[0] = L'\0';
|
|
|
|
//
|
|
// We don't want to treat ! as a special character
|
|
//
|
|
pTokenizer->SetExclaimnationDisabler(TRUE);
|
|
|
|
while (1) {
|
|
|
|
hr = Component(pTokenizer, pObjectInfo);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
hr = GetNextToken(pTokenizer, pObjectInfo, &szToken, &dwToken);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
if (dwToken == TOKEN_COMMA || dwToken == TOKEN_SEMICOLON) {
|
|
continue;
|
|
}
|
|
|
|
if (dwToken == TOKEN_ATSIGN) {
|
|
//
|
|
// Need to return entire dn as username and no domain
|
|
//
|
|
*ppszNTLMUser = AllocADsStr(pszDN);
|
|
|
|
if (!*ppszNTLMUser) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
*ppszNTLMDomain = AllocADsStr(L"");
|
|
|
|
if (!*ppszNTLMDomain) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
if (dwToken == TOKEN_END) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
dwNumComponents = pObjectInfo->NumComponents;
|
|
|
|
if (dwNumComponents) {
|
|
|
|
pszSrcComp = pObjectInfo->ComponentArray[0].szComponent;
|
|
pszSrcValue = pObjectInfo->ComponentArray[0].szValue;
|
|
|
|
if (pszSrcComp) {
|
|
|
|
// if there was a LHS as in CN=krishnag
|
|
|
|
if (!_wcsicmp(pszSrcComp, L"CN") && pszSrcValue) {
|
|
|
|
pszUserName = (LPWSTR) AllocADsMem((wcslen(pszSrcValue) + 1) * sizeof(WCHAR));
|
|
if(!pszUserName)
|
|
{
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
wcscpy(pszUserName, pszSrcValue);
|
|
}else if (!pszSrcValue){
|
|
|
|
pszUserName = (LPWSTR) AllocADsMem((wcslen(pszSrcComp) + 1) * sizeof(WCHAR));
|
|
if(!pszUserName)
|
|
{
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
wcscpy(pszUserName, pszSrcComp);
|
|
}
|
|
}
|
|
|
|
for (i = 1; i < dwNumComponents; i++) {
|
|
|
|
pszSrcComp = pObjectInfo->ComponentArray[i].szComponent;
|
|
pszSrcValue = pObjectInfo->ComponentArray[i].szValue;
|
|
if (pszSrcComp) {
|
|
|
|
// if there was a LHS as in DC=NTWKSTA
|
|
|
|
if (!_wcsicmp(pszSrcComp, L"DC")) {
|
|
|
|
if((!pszSrcValue) || (pszSrcValue && (wcslen(pszSrcValue) >= MAX_PATH)))
|
|
{
|
|
BAIL_ON_FAILURE(hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
|
|
}
|
|
wcscpy(szDomainName, pszSrcValue);
|
|
break;
|
|
}else if (!pszSrcValue){
|
|
|
|
if(wcslen(pszSrcComp) >= MAX_PATH)
|
|
{
|
|
BAIL_ON_FAILURE(hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
|
|
}
|
|
wcscpy(szDomainName, pszSrcComp);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pszUserName) {
|
|
|
|
if (szDomainName[0]) {
|
|
|
|
pszDomainName = AllocADsStr(szDomainName);
|
|
if (!pszDomainName) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
}
|
|
|
|
}else {
|
|
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
|
|
*ppszNTLMUser = pszUserName;
|
|
*ppszNTLMDomain = pszDomainName;
|
|
|
|
exit:
|
|
//
|
|
// Clean up the parser object
|
|
//
|
|
FreeObjectInfo(pObjectInfo);
|
|
|
|
return(hr);
|
|
|
|
error:
|
|
|
|
//
|
|
// Clean up the parser object
|
|
//
|
|
FreeObjectInfo(pObjectInfo);
|
|
|
|
|
|
if (pszUserName) {
|
|
FreeADsStr(pszUserName);
|
|
}
|
|
|
|
if (pszDomainName) {
|
|
FreeADsStr(pszDomainName);
|
|
}
|
|
|
|
if (*ppszNTLMUser) {
|
|
FreeADsStr(*ppszNTLMUser);
|
|
}
|
|
|
|
if (*ppszNTLMDomain) {
|
|
FreeADsStr(*ppszNTLMDomain);
|
|
}
|
|
|
|
//
|
|
// Now we just return whatever is returned from the parser.
|
|
//
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//
|
|
// would like to use WinNT codes path cracking codes - but doesn't
|
|
// work. Too messy, need to clean up - move all path mgmt codes to common.
|
|
//
|
|
|
|
HRESULT
|
|
LdapCrackUserDNtoNTLMUser2(
|
|
IN LPWSTR pszDN,
|
|
OUT LPWSTR * ppszNTLMUser,
|
|
OUT LPWSTR * ppszNTLMDomain
|
|
)
|
|
{
|
|
HRESULT hr = E_ADS_BAD_PATHNAME;
|
|
DWORD credStatus = SEC_E_OK;
|
|
|
|
|
|
ASSERT(ppszNTLMUser);
|
|
ASSERT(ppszNTLMDomain);
|
|
|
|
*ppszNTLMUser=NULL;
|
|
*ppszNTLMDomain=NULL;
|
|
|
|
if (!pszDN) {
|
|
RRETURN(S_OK);
|
|
}
|
|
|
|
*ppszNTLMUser = (LPWSTR) AllocADsMem( (wcslen(pszDN)+1) * sizeof(WCHAR) );
|
|
*ppszNTLMDomain = (LPWSTR) AllocADsMem( (wcslen(pszDN)+1) * sizeof(WCHAR) );
|
|
if(!(*ppszNTLMUser) || !(*ppszNTLMDomain))
|
|
{
|
|
hr = ERROR_NOT_ENOUGH_MEMORY;
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
|
|
|
|
//
|
|
// CredUIParseUserName will parse NT4 type name, UPN and MarshalledCredentialReference
|
|
//
|
|
credStatus = CredUIParseUserName(pszDN,
|
|
*ppszNTLMUser,
|
|
wcslen(pszDN) + 1,
|
|
*ppszNTLMDomain,
|
|
wcslen(pszDN) + 1
|
|
);
|
|
|
|
BAIL_ON_FAILURE(hr = HRESULT_FROM_WIN32(credStatus));
|
|
|
|
|
|
RRETURN (hr);
|
|
|
|
|
|
error:
|
|
|
|
if (*ppszNTLMUser) {
|
|
FreeADsStr(*ppszNTLMUser);
|
|
*ppszNTLMUser=NULL;
|
|
}
|
|
|
|
if (*ppszNTLMDomain) {
|
|
FreeADsStr(*ppszNTLMDomain);
|
|
*ppszNTLMDomain=NULL;
|
|
}
|
|
|
|
RRETURN (hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
GetOneToken(
|
|
LPWSTR pszSource,
|
|
LPWSTR * ppszToken,
|
|
DWORD * pdwTokenLen,
|
|
BOOL * pfMore
|
|
)
|
|
{
|
|
LPWSTR pszToken = NULL;
|
|
DWORD dwTokenLen= 0;
|
|
LPWSTR pszCurrChar = pszSource;
|
|
|
|
ASSERT(pszSource);
|
|
ASSERT(ppszToken);
|
|
ASSERT(pdwTokenLen);
|
|
ASSERT(pfMore);
|
|
|
|
*ppszToken=NULL;
|
|
*pdwTokenLen = 0;
|
|
*pfMore = FALSE;
|
|
|
|
|
|
//
|
|
// do we use exception handling code here ? what if caller passed
|
|
// in a bad string -> not terminated?
|
|
//
|
|
|
|
while (*pszCurrChar!=L'\0' && *pszCurrChar!=L'\\') {
|
|
dwTokenLen++;
|
|
pszCurrChar++;
|
|
}
|
|
|
|
pszToken = (LPWSTR) AllocADsMem( (dwTokenLen+1) * sizeof(WCHAR) );
|
|
if (!pszToken) {
|
|
RRETURN(E_OUTOFMEMORY);
|
|
}
|
|
memcpy( pszToken, pszSource, dwTokenLen * sizeof(WCHAR) );
|
|
(pszToken)[dwTokenLen]=L'\0';
|
|
|
|
*pdwTokenLen = dwTokenLen;
|
|
*ppszToken = pszToken;
|
|
*pfMore = (*pszCurrChar==L'\0') ? FALSE : TRUE;
|
|
|
|
RRETURN(S_OK);
|
|
}
|
|
|
|
|
|
VOID
|
|
CheckAndSetExtendedError(
|
|
LDAP *ld,
|
|
HRESULT *perr,
|
|
int ldaperr,
|
|
LDAPMessage *ldapResMsg
|
|
)
|
|
{
|
|
WCHAR pszProviderName[MAX_PATH];
|
|
INT numChars;
|
|
void *option;
|
|
DWORD dwStatus = NO_ERROR;
|
|
DWORD dwErr = NO_ERROR;
|
|
DWORD dwLdapErrParse = NO_ERROR;
|
|
PWCHAR ppszLdapMessage = NULL;
|
|
LPWSTR pszStopChar = NULL;
|
|
|
|
|
|
// first set the dwErr, then we can see if we can get
|
|
// more information from ldap_parse_resultW
|
|
switch (ldaperr) {
|
|
|
|
case LDAP_SUCCESS :
|
|
dwErr = NO_ERROR;
|
|
break;
|
|
|
|
case LDAP_OPERATIONS_ERROR :
|
|
dwErr = ERROR_DS_OPERATIONS_ERROR;
|
|
break;
|
|
|
|
case LDAP_PROTOCOL_ERROR :
|
|
dwErr = ERROR_DS_PROTOCOL_ERROR;
|
|
break;
|
|
|
|
case LDAP_TIMELIMIT_EXCEEDED :
|
|
dwErr = ERROR_DS_TIMELIMIT_EXCEEDED;
|
|
break;
|
|
|
|
case LDAP_SIZELIMIT_EXCEEDED :
|
|
dwErr = ERROR_DS_SIZELIMIT_EXCEEDED;
|
|
break;
|
|
|
|
case LDAP_COMPARE_FALSE :
|
|
dwErr = ERROR_DS_COMPARE_FALSE;
|
|
break;
|
|
|
|
case LDAP_COMPARE_TRUE :
|
|
dwErr = ERROR_DS_COMPARE_TRUE;
|
|
break;
|
|
|
|
case LDAP_AUTH_METHOD_NOT_SUPPORTED :
|
|
dwErr = ERROR_DS_AUTH_METHOD_NOT_SUPPORTED;
|
|
break;
|
|
|
|
case LDAP_STRONG_AUTH_REQUIRED :
|
|
dwErr = ERROR_DS_STRONG_AUTH_REQUIRED;
|
|
break;
|
|
|
|
// LDAP_REFERRAL_V2 has same value as LDAP_PARTIAL_RESULTS
|
|
case LDAP_PARTIAL_RESULTS :
|
|
//
|
|
// If the referral chasing is off for the handle
|
|
// we really don't worry about this error
|
|
//
|
|
dwStatus = ldap_get_option(
|
|
ld,
|
|
LDAP_OPT_REFERRALS,
|
|
&(option)
|
|
);
|
|
|
|
if (dwStatus == NO_ERROR && option == LDAP_OPT_OFF) {
|
|
dwErr = NO_ERROR;
|
|
}
|
|
else {
|
|
dwErr = ERROR_MORE_DATA;
|
|
}
|
|
|
|
break ;
|
|
|
|
|
|
case LDAP_REFERRAL :
|
|
dwErr = ERROR_DS_REFERRAL;
|
|
break;
|
|
|
|
case LDAP_ADMIN_LIMIT_EXCEEDED :
|
|
dwErr = ERROR_DS_ADMIN_LIMIT_EXCEEDED;
|
|
break;
|
|
|
|
case LDAP_UNAVAILABLE_CRIT_EXTENSION :
|
|
dwErr = ERROR_DS_UNAVAILABLE_CRIT_EXTENSION;
|
|
break;
|
|
|
|
case LDAP_CONFIDENTIALITY_REQUIRED :
|
|
dwErr = ERROR_DS_CONFIDENTIALITY_REQUIRED;
|
|
break;
|
|
|
|
case LDAP_NO_SUCH_ATTRIBUTE :
|
|
dwErr = ERROR_DS_NO_ATTRIBUTE_OR_VALUE;
|
|
break;
|
|
|
|
case LDAP_UNDEFINED_TYPE :
|
|
dwErr = ERROR_DS_ATTRIBUTE_TYPE_UNDEFINED;
|
|
break;
|
|
|
|
case LDAP_INAPPROPRIATE_MATCHING :
|
|
dwErr = ERROR_DS_INAPPROPRIATE_MATCHING;
|
|
break;
|
|
|
|
case LDAP_CONSTRAINT_VIOLATION :
|
|
dwErr = ERROR_DS_CONSTRAINT_VIOLATION;
|
|
break;
|
|
|
|
case LDAP_ATTRIBUTE_OR_VALUE_EXISTS :
|
|
dwErr = ERROR_DS_ATTRIBUTE_OR_VALUE_EXISTS;
|
|
break;
|
|
|
|
case LDAP_INVALID_SYNTAX :
|
|
dwErr = ERROR_DS_INVALID_ATTRIBUTE_SYNTAX;
|
|
break;
|
|
|
|
case LDAP_NO_SUCH_OBJECT :
|
|
dwErr = ERROR_DS_NO_SUCH_OBJECT;
|
|
break;
|
|
|
|
case LDAP_ALIAS_PROBLEM :
|
|
dwErr = ERROR_DS_ALIAS_PROBLEM;
|
|
break;
|
|
|
|
case LDAP_INVALID_DN_SYNTAX :
|
|
dwErr = ERROR_DS_INVALID_DN_SYNTAX;
|
|
break;
|
|
|
|
case LDAP_IS_LEAF :
|
|
dwErr = ERROR_DS_IS_LEAF;
|
|
break;
|
|
|
|
case LDAP_ALIAS_DEREF_PROBLEM :
|
|
dwErr = ERROR_DS_ALIAS_DEREF_PROBLEM;
|
|
break;
|
|
|
|
case LDAP_INAPPROPRIATE_AUTH :
|
|
dwErr = ERROR_DS_INAPPROPRIATE_AUTH;
|
|
break;
|
|
|
|
case LDAP_INVALID_CREDENTIALS :
|
|
dwErr = ERROR_LOGON_FAILURE;
|
|
break;
|
|
|
|
case LDAP_INSUFFICIENT_RIGHTS :
|
|
dwErr = ERROR_ACCESS_DENIED;
|
|
break;
|
|
|
|
case LDAP_BUSY :
|
|
dwErr = ERROR_DS_BUSY;
|
|
break;
|
|
|
|
case LDAP_UNAVAILABLE :
|
|
dwErr = ERROR_DS_UNAVAILABLE;
|
|
break;
|
|
|
|
case LDAP_UNWILLING_TO_PERFORM :
|
|
dwErr = ERROR_DS_UNWILLING_TO_PERFORM;
|
|
break;
|
|
|
|
case LDAP_LOOP_DETECT :
|
|
dwErr = ERROR_DS_LOOP_DETECT;
|
|
break;
|
|
|
|
case LDAP_NAMING_VIOLATION :
|
|
dwErr = ERROR_DS_NAMING_VIOLATION;
|
|
break;
|
|
|
|
case LDAP_OBJECT_CLASS_VIOLATION :
|
|
dwErr = ERROR_DS_OBJ_CLASS_VIOLATION;
|
|
break;
|
|
|
|
case LDAP_NOT_ALLOWED_ON_NONLEAF :
|
|
dwErr = ERROR_DS_CANT_ON_NON_LEAF;
|
|
break;
|
|
|
|
case LDAP_NOT_ALLOWED_ON_RDN :
|
|
dwErr = ERROR_DS_CANT_ON_RDN;
|
|
break;
|
|
|
|
case LDAP_ALREADY_EXISTS :
|
|
dwErr = ERROR_OBJECT_ALREADY_EXISTS;
|
|
break;
|
|
|
|
case LDAP_NO_OBJECT_CLASS_MODS :
|
|
dwErr = ERROR_DS_CANT_MOD_OBJ_CLASS;
|
|
break;
|
|
|
|
case LDAP_RESULTS_TOO_LARGE :
|
|
dwErr = ERROR_DS_OBJECT_RESULTS_TOO_LARGE;
|
|
break;
|
|
|
|
case LDAP_AFFECTS_MULTIPLE_DSAS :
|
|
dwErr = ERROR_DS_AFFECTS_MULTIPLE_DSAS;
|
|
break;
|
|
|
|
case LDAP_OTHER :
|
|
dwErr = ERROR_GEN_FAILURE;
|
|
break;
|
|
|
|
case LDAP_SERVER_DOWN :
|
|
dwErr = ERROR_DS_SERVER_DOWN;
|
|
break;
|
|
|
|
case LDAP_LOCAL_ERROR :
|
|
dwErr = ERROR_DS_LOCAL_ERROR;
|
|
break;
|
|
|
|
case LDAP_ENCODING_ERROR :
|
|
dwErr = ERROR_DS_ENCODING_ERROR;
|
|
break;
|
|
|
|
case LDAP_DECODING_ERROR :
|
|
dwErr = ERROR_DS_DECODING_ERROR;
|
|
break;
|
|
|
|
case LDAP_TIMEOUT :
|
|
dwErr = ERROR_TIMEOUT;
|
|
break;
|
|
|
|
case LDAP_AUTH_UNKNOWN :
|
|
dwErr = ERROR_DS_AUTH_UNKNOWN;
|
|
break;
|
|
|
|
case LDAP_FILTER_ERROR :
|
|
dwErr = ERROR_DS_FILTER_UNKNOWN;
|
|
break;
|
|
|
|
case LDAP_USER_CANCELLED :
|
|
dwErr = ERROR_CANCELLED;
|
|
break;
|
|
|
|
case LDAP_PARAM_ERROR :
|
|
dwErr = ERROR_DS_PARAM_ERROR;
|
|
break;
|
|
|
|
case LDAP_NO_MEMORY :
|
|
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
|
|
case LDAP_CONNECT_ERROR :
|
|
dwErr = ERROR_CONNECTION_REFUSED;
|
|
break;
|
|
|
|
case LDAP_NOT_SUPPORTED :
|
|
dwErr = ERROR_DS_NOT_SUPPORTED;
|
|
break;
|
|
|
|
case LDAP_NO_RESULTS_RETURNED :
|
|
dwErr = ERROR_DS_NO_RESULTS_RETURNED;
|
|
break;
|
|
|
|
case LDAP_CONTROL_NOT_FOUND :
|
|
dwErr = ERROR_DS_CONTROL_NOT_FOUND;
|
|
break;
|
|
|
|
case LDAP_MORE_RESULTS_TO_RETURN :
|
|
dwErr = ERROR_MORE_DATA;
|
|
break;
|
|
|
|
case LDAP_CLIENT_LOOP :
|
|
dwErr = ERROR_DS_CLIENT_LOOP;
|
|
break;
|
|
|
|
case LDAP_REFERRAL_LIMIT_EXCEEDED :
|
|
dwErr = ERROR_DS_REFERRAL_LIMIT_EXCEEDED;
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// It may not be a bad idea to add range checking here
|
|
//
|
|
dwErr = (DWORD) LdapMapErrorToWin32(ldaperr);
|
|
|
|
}
|
|
|
|
//
|
|
// Set extended error only if it was not success and the
|
|
// handle was not NULL
|
|
//
|
|
if ((ldaperr != LDAP_SUCCESS) && (ld !=NULL)) {
|
|
|
|
dwStatus = ldap_get_optionW(
|
|
ld,
|
|
LDAP_OPT_SERVER_ERROR,
|
|
(void *)&ppszLdapMessage
|
|
);
|
|
|
|
if (ppszLdapMessage != NULL && dwStatus == NO_ERROR) {
|
|
dwLdapErrParse = wcstoul(ppszLdapMessage, &pszStopChar, 16);
|
|
//
|
|
// If the error code is zero and the message and stopChar
|
|
// point to the same location, we must have invalid data.
|
|
// We also have invalid data if the return value from the
|
|
// call is ULONG_MAX.
|
|
//
|
|
if ((!dwLdapErrParse && (ppszLdapMessage == pszStopChar))
|
|
|| (dwLdapErrParse == (DWORD) -1)
|
|
) {
|
|
dwLdapErrParse = ERROR_INVALID_DATA;
|
|
}
|
|
}
|
|
|
|
ADsSetLastError(
|
|
dwLdapErrParse,
|
|
ppszLdapMessage,
|
|
L"LDAP Provider"
|
|
);
|
|
|
|
if (ppszLdapMessage) {
|
|
LdapMemFree(ppszLdapMessage);
|
|
ppszLdapMessage = NULL;
|
|
}
|
|
|
|
} else {
|
|
|
|
// We need to reset the last error with a well known value
|
|
ADsSetLastError(
|
|
NO_ERROR,
|
|
NULL,
|
|
L"LDAP Provider"
|
|
);
|
|
}
|
|
|
|
|
|
*perr = HRESULT_FROM_WIN32(dwErr);
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// Return TRUE if the error is a connection error. Ie. this bind handle is
|
|
// no longer valid and should not be reused.
|
|
//
|
|
BOOL LdapConnectionErr(
|
|
int err,
|
|
int ldaperr,
|
|
BOOL *fTryRebind
|
|
)
|
|
{
|
|
|
|
(void) fTryRebind ; // currently not used
|
|
|
|
switch (err) {
|
|
|
|
case ERROR_DS_UNAVAILABLE :
|
|
case ERROR_GEN_FAILURE :
|
|
case ERROR_DS_LOCAL_ERROR :
|
|
case ERROR_DS_SERVER_DOWN :
|
|
return TRUE ;
|
|
|
|
default:
|
|
return FALSE ;
|
|
}
|
|
}
|
|
|
|
HRESULT LdapSearchExtSHelper(
|
|
LDAP *ld,
|
|
WCHAR *base,
|
|
int scope,
|
|
WCHAR *filter,
|
|
WCHAR *attrs[],
|
|
int attrsonly,
|
|
PLDAPControlW * ServerControls,
|
|
PLDAPControlW *ClientControls,
|
|
struct l_timeval *Timeout,
|
|
ULONG SizeLimit,
|
|
LDAPMessage **res
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int ldaperr = 0;
|
|
|
|
ldaperr = ldap_search_ext_s( ld,
|
|
base,
|
|
scope,
|
|
filter,
|
|
attrs,
|
|
attrsonly,
|
|
ServerControls,
|
|
ClientControls,
|
|
Timeout,
|
|
SizeLimit,
|
|
res );
|
|
|
|
if (ldaperr) {
|
|
CheckAndSetExtendedError( ld, &hr, ldaperr, *res );
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
HRESULT LdapSearchExtS(
|
|
ADS_LDP *ld,
|
|
WCHAR *base,
|
|
int scope,
|
|
WCHAR *filter,
|
|
WCHAR *attrs[],
|
|
int attrsonly,
|
|
PLDAPControlW * ServerControls,
|
|
PLDAPControlW *ClientControls,
|
|
struct l_timeval *Timeout,
|
|
ULONG SizeLimit,
|
|
LDAPMessage **res
|
|
)
|
|
{
|
|
HRESULT hr = LdapSearchExtSHelper(
|
|
ld->LdapHandle,
|
|
base,
|
|
scope,
|
|
filter,
|
|
attrs,
|
|
attrsonly,
|
|
ServerControls,
|
|
ClientControls,
|
|
Timeout,
|
|
SizeLimit,
|
|
res );
|
|
|
|
if ( LdapConnectionErr(hr, 0, NULL)) {
|
|
|
|
BindCacheInvalidateEntry(ld) ;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT LdapSearchExt(
|
|
ADS_LDP *ld,
|
|
WCHAR *base,
|
|
int scope,
|
|
WCHAR *filter,
|
|
WCHAR *attrs[],
|
|
int attrsonly,
|
|
PLDAPControlW *ServerControls,
|
|
PLDAPControlW *ClientControls,
|
|
ULONG TimeLimit,
|
|
ULONG SizeLimit,
|
|
ULONG *MessageNumber
|
|
)
|
|
{
|
|
char *pszBaseA = NULL;
|
|
int ldaperr = 0;
|
|
HRESULT hr = 0;
|
|
|
|
ldaperr = ldap_search_ext(
|
|
ld->LdapHandle,
|
|
base,
|
|
scope,
|
|
filter,
|
|
attrs,
|
|
attrsonly,
|
|
ServerControls,
|
|
ClientControls,
|
|
TimeLimit,
|
|
SizeLimit,
|
|
MessageNumber
|
|
);
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr);
|
|
|
|
if (LdapConnectionErr(hr, 0, NULL)) {
|
|
|
|
BindCacheInvalidateEntry(ld) ;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
ULONG _cdecl QueryForConnection(
|
|
PLDAP PrimaryConnection,
|
|
PLDAP ReferralFromConnection,
|
|
PWCHAR NewDN,
|
|
PCHAR HostName,
|
|
ULONG PortNumber,
|
|
PVOID SecAuthIdentity, // if null, use CurrentUser below
|
|
PVOID CurrentUserToken, // pointer to current user's LUID
|
|
PLDAP *ConnectionToUse
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
PADS_LDP pCacheEntry = NULL;
|
|
LPWSTR pszServer = NULL;
|
|
|
|
PSEC_WINNT_AUTH_IDENTITY pSecAuthIdentity = (PSEC_WINNT_AUTH_IDENTITY) SecAuthIdentity;
|
|
PLUID pLuid = (PLUID) CurrentUserToken;
|
|
CCredentials Credentials;
|
|
|
|
if (ConnectionToUse) {
|
|
*ConnectionToUse = NULL;
|
|
}
|
|
|
|
if (!pLuid) {
|
|
goto error;
|
|
}
|
|
|
|
if (pSecAuthIdentity) {
|
|
hr = Credentials.SetUserName(pSecAuthIdentity->User);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
hr = Credentials.SetPassword(pSecAuthIdentity->Password);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
Credentials.SetAuthFlags(ADS_SECURE_AUTHENTICATION);
|
|
}
|
|
|
|
if (ConvertToUnicode(HostName, &pszServer ))
|
|
goto error;
|
|
|
|
if (!pszServer) {
|
|
//
|
|
// Cannot do a lookup without server name.
|
|
//
|
|
goto error;
|
|
}
|
|
if (pCacheEntry = BindCacheLookup(pszServer, *pLuid, ReservedLuid, Credentials, PortNumber)) {
|
|
|
|
*ConnectionToUse = pCacheEntry->LdapHandle ;
|
|
}
|
|
|
|
error:
|
|
|
|
if (pszServer) {
|
|
FreeADsMem(pszServer);
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
BOOLEAN _cdecl NotifyNewConnection(
|
|
PLDAP PrimaryConnection,
|
|
PLDAP ReferralFromConnection,
|
|
PWCHAR NewDN,
|
|
PCHAR HostName,
|
|
PLDAP NewConnection,
|
|
ULONG PortNumber,
|
|
PVOID SecAuthIdentity, // if null, use CurrentUser below
|
|
PVOID CurrentUser, // pointer to current user's LUID
|
|
ULONG ErrorCodeFromBind
|
|
)
|
|
{
|
|
|
|
PADS_LDP pCacheEntry = NULL;
|
|
PADS_LDP pPrimaryEntry = NULL;
|
|
HRESULT hr = S_OK;
|
|
LPWSTR pszServer = NULL;
|
|
DWORD err;
|
|
|
|
BOOLEAN ret = FALSE;
|
|
|
|
PSEC_WINNT_AUTH_IDENTITY pSecAuthIdentity = (PSEC_WINNT_AUTH_IDENTITY) SecAuthIdentity;
|
|
PLUID pLuid = (PLUID) CurrentUser;
|
|
CCredentials Credentials;
|
|
|
|
if (!pLuid || !NewConnection) {
|
|
goto error;
|
|
}
|
|
|
|
//
|
|
// Get the cache entry corresponding to the Primary Connection
|
|
// This is necessary so that we can link the new connection to the primary
|
|
// connection so that we can decrease the reference
|
|
//
|
|
|
|
if ((pPrimaryEntry = GetCacheEntry(PrimaryConnection)) == NULL) {
|
|
goto error;
|
|
}
|
|
|
|
err = BindCacheAllocEntry(&pCacheEntry) ;
|
|
|
|
if (err != NO_ERROR) {
|
|
goto error;
|
|
}
|
|
|
|
if ( ConvertToUnicode(HostName, &pszServer))
|
|
goto error;
|
|
|
|
if (pSecAuthIdentity) {
|
|
hr = Credentials.SetUserName(pSecAuthIdentity->User);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
hr = Credentials.SetPassword(pSecAuthIdentity->Password);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
Credentials.SetAuthFlags(ADS_SECURE_AUTHENTICATION);
|
|
}
|
|
|
|
pCacheEntry->LdapHandle = NewConnection;
|
|
|
|
err = BindCacheAdd(pszServer, *pLuid, ReservedLuid, Credentials, PortNumber, pCacheEntry) ;
|
|
if (err == NO_ERROR) {
|
|
|
|
if (!AddReferralLink(pPrimaryEntry, pCacheEntry)) {
|
|
//
|
|
// If the referral link could not be succesfully added, we don't
|
|
// want to keep this connection.
|
|
//
|
|
BindCacheDeref(pCacheEntry);
|
|
goto error;
|
|
}
|
|
|
|
//
|
|
// return TRUE to indicate that we have succesfully cached the handle
|
|
//
|
|
ret = TRUE;
|
|
}
|
|
|
|
error:
|
|
|
|
if (pszServer) {
|
|
FreeADsMem(pszServer);
|
|
}
|
|
|
|
if (!ret && pCacheEntry) {
|
|
FreeADsMem(pCacheEntry);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
ULONG _cdecl DereferenceConnection(
|
|
PLDAP PrimaryConnection,
|
|
PLDAP ConnectionToDereference
|
|
)
|
|
{
|
|
|
|
PADS_LDP pCacheEntry;
|
|
|
|
if (ConnectionToDereference) {
|
|
|
|
if (pCacheEntry = GetCacheEntry(ConnectionToDereference)) {
|
|
LdapCloseObject(pCacheEntry);
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT LdapSearchInitPage(
|
|
ADS_LDP *ld,
|
|
PWCHAR base,
|
|
ULONG scope,
|
|
PWCHAR filter,
|
|
PWCHAR attrs[],
|
|
ULONG attrsonly,
|
|
PLDAPControlW *serverControls,
|
|
PLDAPControlW *clientControls,
|
|
ULONG pageTimeLimit,
|
|
ULONG totalSizeLimit,
|
|
PLDAPSortKeyW *sortKeys,
|
|
PLDAPSearch *ppSearch
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int ldaperr = 0;
|
|
|
|
ADsAssert(ppSearch);
|
|
|
|
*ppSearch = ldap_search_init_page(
|
|
ld->LdapHandle,
|
|
base,
|
|
scope,
|
|
filter,
|
|
attrs,
|
|
attrsonly,
|
|
serverControls,
|
|
clientControls,
|
|
pageTimeLimit,
|
|
totalSizeLimit,
|
|
sortKeys
|
|
);
|
|
|
|
if (*ppSearch == NULL) {
|
|
|
|
ldaperr = LdapGetLastError();
|
|
}
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr );
|
|
|
|
if ( LdapConnectionErr(hr, 0, NULL)) {
|
|
|
|
BindCacheInvalidateEntry(ld) ;
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
HRESULT LdapGetNextPage(
|
|
ADS_LDP *ld,
|
|
PLDAPSearch searchHandle,
|
|
ULONG pageSize,
|
|
ULONG *pMessageNumber
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
int ldaperr = ldap_get_next_page(
|
|
ld->LdapHandle,
|
|
searchHandle,
|
|
pageSize,
|
|
pMessageNumber
|
|
);
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr );
|
|
|
|
if ( LdapConnectionErr(hr, 0, NULL)) {
|
|
|
|
BindCacheInvalidateEntry(ld) ;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT LdapGetNextPageS(
|
|
ADS_LDP *ld,
|
|
PLDAPSearch searchHandle,
|
|
struct l_timeval *timeOut,
|
|
ULONG pageSize,
|
|
ULONG *totalCount,
|
|
LDAPMessage **results
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
int ldaperr = ldap_get_next_page_s(
|
|
ld->LdapHandle,
|
|
searchHandle,
|
|
timeOut,
|
|
pageSize,
|
|
totalCount,
|
|
results
|
|
);
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr );
|
|
|
|
if ( LdapConnectionErr(hr, 0, NULL)) {
|
|
|
|
BindCacheInvalidateEntry(ld) ;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT LdapGetPagedCount(
|
|
ADS_LDP *ld,
|
|
PLDAPSearch searchBlock,
|
|
ULONG *totalCount,
|
|
PLDAPMessage results
|
|
)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
int ldaperr = ldap_get_paged_count(
|
|
ld->LdapHandle,
|
|
searchBlock,
|
|
totalCount,
|
|
results
|
|
);
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr, results );
|
|
|
|
if ( LdapConnectionErr(hr, 0, NULL)) {
|
|
|
|
BindCacheInvalidateEntry(ld) ;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT LdapSearchAbandonPage(
|
|
ADS_LDP *ld,
|
|
PLDAPSearch searchBlock
|
|
)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
int ldaperr = ldap_search_abandon_page(
|
|
ld->LdapHandle,
|
|
searchBlock
|
|
);
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr );
|
|
|
|
if ( LdapConnectionErr(hr, 0, NULL)) {
|
|
|
|
BindCacheInvalidateEntry(ld) ;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT LdapEncodeSortControl(
|
|
ADS_LDP *ld,
|
|
PLDAPSortKeyW *SortKeys,
|
|
PLDAPControlW Control,
|
|
BOOLEAN Criticality
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
int ldaperr = ldap_encode_sort_controlW (
|
|
ld->LdapHandle,
|
|
SortKeys,
|
|
Control,
|
|
Criticality
|
|
);
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr );
|
|
|
|
return hr;
|
|
}
|
|
|
|
BOOL IsGCNamespace(
|
|
LPWSTR szADsPath
|
|
)
|
|
{
|
|
|
|
WCHAR szNamespace[MAX_PATH];
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = GetNamespaceFromADsPath(
|
|
szADsPath,
|
|
szNamespace
|
|
);
|
|
if (FAILED(hr))
|
|
return FALSE;
|
|
|
|
return (_wcsicmp(szNamespace, szGCNamespaceName) == 0);
|
|
|
|
}
|
|
|
|
|
|
HRESULT LdapCreatePageControl(
|
|
ADS_LDP *ld,
|
|
ULONG dwPageSize,
|
|
struct berval *Cookie,
|
|
BOOL fIsCritical,
|
|
PLDAPControl *Control // Use LdapControlFree to free
|
|
)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
int ldaperr = ldap_create_page_control(
|
|
ld->LdapHandle,
|
|
dwPageSize,
|
|
Cookie,
|
|
(UCHAR) fIsCritical,
|
|
Control
|
|
);
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr );
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT LdapParsePageControl(
|
|
ADS_LDP *ld,
|
|
PLDAPControl *ServerControls,
|
|
ULONG *TotalCount,
|
|
struct berval **Cookie // Use BerBvFree to free
|
|
)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
int ldaperr = ldap_parse_page_control(
|
|
ld->LdapHandle,
|
|
ServerControls,
|
|
TotalCount,
|
|
Cookie
|
|
);
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr );
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT LdapCreateVLVControl(
|
|
ADS_LDP *pld,
|
|
PLDAPVLVInfo pVLVInfo,
|
|
UCHAR fCritical,
|
|
PLDAPControl *ppControl // Use LdapControlFree to free
|
|
)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
int ldaperr = ldap_create_vlv_control(
|
|
pld->LdapHandle,
|
|
pVLVInfo,
|
|
fCritical,
|
|
ppControl
|
|
);
|
|
|
|
CheckAndSetExtendedError( pld->LdapHandle, &hr, ldaperr );
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT LdapParseVLVControl(
|
|
ADS_LDP *pld,
|
|
PLDAPControl *ppServerControls,
|
|
ULONG *pTargetPos,
|
|
ULONG *pListCount,
|
|
struct berval **ppCookie // Use BerBvFree to free
|
|
)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
int ldaperr = ldap_parse_vlv_control(
|
|
pld->LdapHandle,
|
|
ppServerControls,
|
|
pTargetPos,
|
|
pListCount,
|
|
ppCookie,
|
|
NULL
|
|
);
|
|
|
|
CheckAndSetExtendedError( pld->LdapHandle, &hr, ldaperr );
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT LdapParseResult(
|
|
ADS_LDP *ld,
|
|
LDAPMessage *ResultMessage,
|
|
ULONG *ReturnCode OPTIONAL, // returned by server
|
|
PWCHAR *MatchedDNs OPTIONAL, // free with LdapMemFree
|
|
PWCHAR *ErrorMessage OPTIONAL, // free with LdapMemFree
|
|
PWCHAR **Referrals OPTIONAL, // free with LdapValueFree
|
|
PLDAPControlW **ServerControls OPTIONAL, // free with LdapFreeControls
|
|
BOOL Freeit
|
|
)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
int ldaperr = ldap_parse_result(
|
|
ld->LdapHandle,
|
|
ResultMessage,
|
|
ReturnCode,
|
|
MatchedDNs,
|
|
ErrorMessage,
|
|
Referrals,
|
|
ServerControls,
|
|
(BOOLEAN) Freeit
|
|
);
|
|
|
|
CheckAndSetExtendedError( ld->LdapHandle, &hr, ldaperr, ResultMessage );
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
void
|
|
LdapControlsFree(
|
|
PLDAPControl *ppControl
|
|
)
|
|
{
|
|
ldap_controls_free( ppControl );
|
|
|
|
}
|
|
|
|
void
|
|
LdapControlFree(
|
|
PLDAPControl pControl
|
|
)
|
|
{
|
|
ldap_control_free( pControl );
|
|
|
|
}
|
|
|
|
void
|
|
BerBvFree(
|
|
struct berval *bv
|
|
)
|
|
{
|
|
ber_bvfree( bv );
|
|
|
|
}
|
|
|
|
//
|
|
// This function looks at the LDAP error code to see if we
|
|
// should try and bind again using a lower version level or
|
|
// alternate mechanism
|
|
//
|
|
BOOLEAN
|
|
LDAPCodeWarrantsRetry(
|
|
HRESULT hr
|
|
)
|
|
{
|
|
BOOLEAN fretVal = FALSE;
|
|
|
|
switch (hr) {
|
|
|
|
case HRESULT_FROM_WIN32(ERROR_DS_OPERATIONS_ERROR) :
|
|
fretVal = TRUE;
|
|
break;
|
|
|
|
case HRESULT_FROM_WIN32(ERROR_DS_PROTOCOL_ERROR) :
|
|
fretVal = TRUE;
|
|
break;
|
|
|
|
case HRESULT_FROM_WIN32(ERROR_DS_AUTH_METHOD_NOT_SUPPORTED) :
|
|
fretVal = TRUE;
|
|
break;
|
|
|
|
case HRESULT_FROM_WIN32(ERROR_DS_AUTH_UNKNOWN) :
|
|
fretVal = TRUE;
|
|
break;
|
|
|
|
case HRESULT_FROM_WIN32(ERROR_DS_NOT_SUPPORTED) :
|
|
fretVal = TRUE;
|
|
break;
|
|
|
|
default:
|
|
fretVal = FALSE;
|
|
break;
|
|
} // end case
|
|
|
|
return fretVal;
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
LdapcSetStickyServer(
|
|
LPWSTR pszDomainName,
|
|
LPWSTR pszServerName
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (gpszServerName) {
|
|
FreeADsStr(gpszServerName);
|
|
gpszServerName = NULL;
|
|
}
|
|
|
|
if (pszServerName) {
|
|
gpszServerName = AllocADsStr(pszServerName);
|
|
|
|
if (!gpszServerName) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
}
|
|
|
|
if (gpszDomainName) {
|
|
FreeADsStr(gpszDomainName);
|
|
gpszDomainName = NULL;
|
|
}
|
|
|
|
if (pszDomainName) {
|
|
gpszDomainName = AllocADsStr(pszDomainName);
|
|
|
|
if (!gpszDomainName) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
}
|
|
|
|
RRETURN(hr);
|
|
|
|
error :
|
|
|
|
if (gpszServerName) {
|
|
FreeADsStr(gpszServerName);
|
|
gpszServerName = NULL;
|
|
}
|
|
|
|
if (gpszDomainName) {
|
|
FreeADsStr(gpszDomainName);
|
|
gpszDomainName = NULL;
|
|
}
|
|
|
|
RRETURN(hr);
|
|
|
|
}
|