Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1504 lines
40 KiB

//+---------------------------------------------------------------------------
//
// Microsoft Windows NT Security
// Copyright (C) Microsoft Corporation, 1997 - 1999
//
// File: ldapsp.cpp
//
// Contents: LDAP Scheme Provider for Remote Object Retrieval
//
// History: 23-Jul-97 kirtd Created
// 01-Jan-02 philh Changed to internally use UNICODE Urls
//
//----------------------------------------------------------------------------
#include <global.hxx>
#ifndef INTERNET_MAX_PATH_LENGTH
#define INTERNET_MAX_PATH_LENGTH 2048
#endif
//+---------------------------------------------------------------------------
//
// Function: LdapRetrieveEncodedObject
//
// Synopsis: retrieve encoded object via LDAP protocol
//
//----------------------------------------------------------------------------
BOOL WINAPI LdapRetrieveEncodedObject (
IN LPCWSTR pwszUrl,
IN LPCSTR pszObjectOid,
IN DWORD dwRetrievalFlags,
IN DWORD dwTimeout,
OUT PCRYPT_BLOB_ARRAY pObject,
OUT PFN_FREE_ENCODED_OBJECT_FUNC* ppfnFreeObject,
OUT LPVOID* ppvFreeContext,
IN HCRYPTASYNC hAsyncRetrieve,
IN PCRYPT_CREDENTIALS pCredentials,
IN PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
{
BOOL fResult;
IObjectRetriever* por = NULL;
if ( !( dwRetrievalFlags & CRYPT_ASYNC_RETRIEVAL ) )
{
por = new CLdapSynchronousRetriever;
}
if ( por == NULL )
{
SetLastError( (DWORD) E_OUTOFMEMORY );
return( FALSE );
}
fResult = por->RetrieveObjectByUrl(
pwszUrl,
pszObjectOid,
dwRetrievalFlags,
dwTimeout,
(LPVOID *)pObject,
ppfnFreeObject,
ppvFreeContext,
hAsyncRetrieve,
pCredentials,
NULL,
pAuxInfo
);
por->Release();
return( fResult );
}
//+---------------------------------------------------------------------------
//
// Function: LdapFreeEncodedObject
//
// Synopsis: free encoded object retrieved via LdapRetrieveEncodedObject
//
//----------------------------------------------------------------------------
VOID WINAPI LdapFreeEncodedObject (
IN LPCSTR pszObjectOid,
IN PCRYPT_BLOB_ARRAY pObject,
IN LPVOID pvFreeContext
)
{
assert( pvFreeContext == NULL );
LdapFreeCryptBlobArray( pObject );
}
//+---------------------------------------------------------------------------
//
// Function: LdapCancelAsyncRetrieval
//
// Synopsis: cancel asynchronous object retrieval
//
//----------------------------------------------------------------------------
BOOL WINAPI LdapCancelAsyncRetrieval (
IN HCRYPTASYNC hAsyncRetrieve
)
{
SetLastError( (DWORD) E_NOTIMPL );
return( FALSE );
}
//+---------------------------------------------------------------------------
//
// Member: CLdapSynchronousRetriever::CLdapSynchronousRetriever, public
//
// Synopsis: Constructor
//
//----------------------------------------------------------------------------
CLdapSynchronousRetriever::CLdapSynchronousRetriever ()
{
m_cRefs = 1;
}
//+---------------------------------------------------------------------------
//
// Member: CLdapSynchronousRetriever::~CLdapSynchronousRetriever, public
//
// Synopsis: Destructor
//
//----------------------------------------------------------------------------
CLdapSynchronousRetriever::~CLdapSynchronousRetriever ()
{
}
//+---------------------------------------------------------------------------
//
// Member: CLdapSynchronousRetriever::AddRef, public
//
// Synopsis: IRefCountedObject::AddRef
//
//----------------------------------------------------------------------------
VOID
CLdapSynchronousRetriever::AddRef ()
{
InterlockedIncrement( (LONG *)&m_cRefs );
}
//+---------------------------------------------------------------------------
//
// Member: CLdapSynchronousRetriever::Release, public
//
// Synopsis: IRefCountedObject::Release
//
//----------------------------------------------------------------------------
VOID
CLdapSynchronousRetriever::Release ()
{
if ( InterlockedDecrement( (LONG *)&m_cRefs ) == 0 )
{
delete this;
}
}
//+---------------------------------------------------------------------------
//
// Member: CLdapSynchronousRetriever::RetrieveObjectByUrl, public
//
// Synopsis: IObjectRetriever::RetrieveObjectByUrl
//
//----------------------------------------------------------------------------
BOOL
CLdapSynchronousRetriever::RetrieveObjectByUrl (
LPCWSTR pwszUrl,
LPCSTR pszObjectOid,
DWORD dwRetrievalFlags,
DWORD dwTimeout,
LPVOID* ppvObject,
PFN_FREE_ENCODED_OBJECT_FUNC* ppfnFreeObject,
LPVOID* ppvFreeContext,
HCRYPTASYNC hAsyncRetrieve,
PCRYPT_CREDENTIALS pCredentials,
LPVOID pvVerify,
PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
{
BOOL fResult;
DWORD LastError = 0;
LDAP_URL_COMPONENTS LdapUrlComponents;
LDAP* pld = NULL;
BOOL fLdapUrlCracked = FALSE;
assert( hAsyncRetrieve == NULL );
if ( dwRetrievalFlags & CRYPT_CACHE_ONLY_RETRIEVAL )
{
return( SchemeRetrieveCachedCryptBlobArray(
pwszUrl,
dwRetrievalFlags,
(PCRYPT_BLOB_ARRAY)ppvObject,
ppfnFreeObject,
ppvFreeContext,
pAuxInfo
) );
}
fResult = LdapCrackUrl( pwszUrl, &LdapUrlComponents );
#if DBG
if ( fResult == TRUE )
{
LdapDisplayUrlComponents( &LdapUrlComponents );
}
#endif
if ( fResult == TRUE )
{
fLdapUrlCracked = TRUE;
if ( dwRetrievalFlags & CRYPT_LDAP_SCOPE_BASE_ONLY_RETRIEVAL )
{
if ( LdapUrlComponents.Scope != LDAP_SCOPE_BASE )
{
fResult = FALSE;
SetLastError( (DWORD) E_INVALIDARG );
}
}
}
if ( fResult == TRUE )
{
DWORD iAuth;
if ( dwRetrievalFlags &
(CRYPT_LDAP_AREC_EXCLUSIVE_RETRIEVAL |
CRYPT_LDAP_SIGN_RETRIEVAL) )
{
// Only attempt AUTH_SSPI binds
iAuth = 1;
}
else
{
// First attempt AUTH_SIMPLE bind. If that fails or returns
// nothing, then, attempt AUTH_SSPI bind.
iAuth = 0;
}
for ( ; iAuth < 2; iAuth++)
{
fResult = LdapGetBindings(
LdapUrlComponents.pwszHost,
LdapUrlComponents.Port,
dwRetrievalFlags,
0 == iAuth ? LDAP_BIND_AUTH_SIMPLE_ENABLE_FLAG :
LDAP_BIND_AUTH_SSPI_ENABLE_FLAG,
dwTimeout,
pCredentials,
&pld
);
if ( fResult == TRUE )
{
fResult = LdapSendReceiveUrlRequest(
pld,
&LdapUrlComponents,
dwRetrievalFlags,
dwTimeout,
(PCRYPT_BLOB_ARRAY)ppvObject,
pAuxInfo
);
if ( fResult == TRUE )
{
break;
}
else
{
LastError = GetLastError();
LdapFreeBindings( pld );
pld = NULL;
SetLastError( LastError );
}
}
}
}
if ( fResult == TRUE )
{
if ( !( dwRetrievalFlags & CRYPT_DONT_CACHE_RESULT ) )
{
fResult = SchemeCacheCryptBlobArray(
pwszUrl,
dwRetrievalFlags,
(PCRYPT_BLOB_ARRAY)ppvObject,
pAuxInfo
);
if ( fResult == FALSE )
{
LdapFreeEncodedObject(
pszObjectOid,
(PCRYPT_BLOB_ARRAY)ppvObject,
NULL
);
}
}
else
{
SchemeRetrieveUncachedAuxInfo( pAuxInfo );
}
}
if ( fResult == TRUE )
{
*ppfnFreeObject = LdapFreeEncodedObject;
*ppvFreeContext = NULL;
}
else
{
LastError = GetLastError();
}
if ( fLdapUrlCracked == TRUE )
{
LdapFreeUrlComponents( &LdapUrlComponents );
}
LdapFreeBindings( pld );
SetLastError( LastError );
return( fResult );
}
//+---------------------------------------------------------------------------
//
// Member: CLdapSynchronousRetriever::CancelAsyncRetrieval, public
//
// Synopsis: IObjectRetriever::CancelAsyncRetrieval
//
//----------------------------------------------------------------------------
BOOL
CLdapSynchronousRetriever::CancelAsyncRetrieval ()
{
SetLastError( (DWORD) E_NOTIMPL );
return( FALSE );
}
//+---------------------------------------------------------------------------
//
// Function: LdapCrackUrl
//
// Synopsis: Crack an LDAP URL into its relevant parts. The result must
// be freed using LdapFreeUrlComponents
//
//----------------------------------------------------------------------------
BOOL
LdapCrackUrl (
LPCWSTR pwszUrl,
PLDAP_URL_COMPONENTS pLdapUrlComponents
)
{
BOOL fResult = TRUE;
LPWSTR pwszHostInfo = NULL;
LPWSTR pwszDN = NULL;
LPWSTR pwszAttrList = NULL;
LPWSTR pwszScope = NULL;
LPWSTR pwszFilter = NULL;
LPWSTR pwszToken = NULL;
WCHAR pwsz[INTERNET_MAX_PATH_LENGTH+1];
ULONG cchUrl = INTERNET_MAX_PATH_LENGTH;
//
// Capture the URL and initialize the out parameter
//
__try
{
HRESULT hr;
#if 0
// UrlCanonicalizeW() moves stuff after the # character to
// the end of Url.
hr = UrlCanonicalizeW(
pwszUrl,
pwsz,
&cchUrl,
URL_UNESCAPE | URL_WININET_COMPATIBILITY
);
#else
// UrlUnescapeW() handles the # character properly
hr = UrlUnescapeW(
(LPWSTR) pwszUrl,
pwsz,
&cchUrl,
0
);
#endif
if (S_OK != hr)
{
SetLastError( (DWORD) hr );
return( FALSE );
}
if ( pwsz[0] == L'\0' )
{
SetLastError( (DWORD) E_INVALIDARG );
return( FALSE );
}
pwsz[sizeof(pwsz)/sizeof(pwsz[0]) - 1] = L'\0';
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
SetLastError( GetExceptionCode() );
return( FALSE );
}
memset( pLdapUrlComponents, 0, sizeof( LDAP_URL_COMPONENTS ) );
//
// Find the host
//
pwszHostInfo = pwsz + wcslen( L"ldap://" );
if ( *pwszHostInfo == L'/' )
{
pwszToken = pwszHostInfo + 1;
pwszHostInfo = NULL;
}
else
{
#if 0
pwszHostInfo = wcstok( pszHostInfo, "/" );
#else
pwszToken = pwszHostInfo;
while ( ( *pwszToken != L'\0' ) && ( *pwszToken != L'/' ) )
pwszToken++;
if ( *pwszToken == L'/' )
{
*pwszToken = L'\0';
pwszToken += 1;
}
while ( *pwszToken == L'/' )
pwszToken++;
#endif
}
//
// Find the DN
//
if ( pwszToken != NULL )
{
pwszDN = L"";
if ( *pwszToken != L'\0' )
{
if ( *pwszToken == L'?' )
{
pwszToken += 1;
}
else
{
pwszDN = pwszToken;
do
{
pwszToken += 1;
}
while ( ( *pwszToken != L'\0' ) && ( *pwszToken != L'?' ) );
if ( *pwszToken == L'?' )
{
*pwszToken = L'\0';
pwszToken += 1;
}
}
}
}
else
{
pwszDN = wcstok( pwszToken, L"?" );
pwszToken = NULL;
if ( pwszDN == NULL )
{
SetLastError( (DWORD) E_INVALIDARG );
return( FALSE );
}
}
//
// Check for attributes
//
if ( pwszToken != NULL )
{
if ( *pwszToken == L'?' )
{
pwszAttrList = L"";
pwszToken += 1;
}
else if ( *pwszToken == L'\0' )
{
pwszAttrList = NULL;
}
else
{
pwszAttrList = wcstok( pwszToken, L"?" );
pwszToken = NULL;
}
}
else
{
pwszAttrList = wcstok( NULL, L"?" );
}
//
// Check for a scope and filter
//
if ( pwszAttrList != NULL )
{
pwszScope = wcstok( pwszToken, L"?" );
if ( pwszScope != NULL )
{
pwszFilter = wcstok( NULL, L"?" );
}
}
if ( pwszScope == NULL )
{
pwszScope = L"base";
}
if ( pwszFilter == NULL )
{
pwszFilter = L"(objectClass=*)";
}
//
// Now we build up our URL components
//
fResult = LdapParseCrackedHost( pwszHostInfo, pLdapUrlComponents );
if ( fResult == TRUE )
{
fResult = LdapParseCrackedDN( pwszDN, pLdapUrlComponents );
}
if ( fResult == TRUE )
{
fResult = LdapParseCrackedAttributeList(
pwszAttrList,
pLdapUrlComponents
);
}
if ( fResult == TRUE )
{
fResult = LdapParseCrackedScopeAndFilter(
pwszScope,
pwszFilter,
pLdapUrlComponents
);
}
if ( fResult != TRUE )
{
LdapFreeUrlComponents( pLdapUrlComponents );
}
return( fResult );
}
//+---------------------------------------------------------------------------
//
// Function: LdapParseCrackedHost
//
// Synopsis: Parse the cracked host string (pszHost is modified)
//
//----------------------------------------------------------------------------
BOOL
LdapParseCrackedHost (
LPWSTR pwszHost,
PLDAP_URL_COMPONENTS pLdapUrlComponents
)
{
LPWSTR pwszPort;
if ( pwszHost != NULL )
{
pwszHost = wcstok( pwszHost, L" " );
}
if ( pwszHost == NULL )
{
pLdapUrlComponents->pwszHost = NULL;
pLdapUrlComponents->Port = LDAP_PORT;
return( TRUE );
}
// See if multiple host names are present
if ( NULL != wcstok( NULL, L" " ) )
{
SetLastError( (DWORD) E_INVALIDARG );
return( FALSE );
}
pwszPort = wcschr( pwszHost, L':' );
if ( pwszPort != NULL )
{
*pwszPort = L'\0';
pwszPort++;
}
pLdapUrlComponents->pwszHost = new WCHAR [wcslen( pwszHost ) + 1];
if ( pLdapUrlComponents->pwszHost == NULL )
{
SetLastError( (DWORD) E_OUTOFMEMORY );
return( FALSE );
}
wcscpy( pLdapUrlComponents->pwszHost, pwszHost );
pLdapUrlComponents->Port = 0;
if ( pwszPort != NULL )
{
pLdapUrlComponents->Port = _wtol( pwszPort );
}
if ( pLdapUrlComponents->Port == 0 )
{
pLdapUrlComponents->Port = LDAP_PORT;
}
return( TRUE );
}
//+---------------------------------------------------------------------------
//
// Function: LdapParseCrackedDN
//
// Synopsis: Parse the cracked DN
//
//----------------------------------------------------------------------------
BOOL
LdapParseCrackedDN (
LPWSTR pwszDN,
PLDAP_URL_COMPONENTS pLdapUrlComponents
)
{
pLdapUrlComponents->pwszDN = new WCHAR [wcslen( pwszDN ) + 1];
if ( pLdapUrlComponents->pwszDN == NULL )
{
SetLastError( (DWORD) E_OUTOFMEMORY );
return( FALSE );
}
wcscpy( pLdapUrlComponents->pwszDN, pwszDN );
return( TRUE );
}
//+---------------------------------------------------------------------------
//
// Function: LdapParseCrackedAttributeList
//
// Synopsis: Parse the cracked attribute list
//
//----------------------------------------------------------------------------
BOOL
LdapParseCrackedAttributeList (
LPWSTR pwszAttrList,
PLDAP_URL_COMPONENTS pLdapUrlComponents
)
{
LPWSTR pwsz;
LPWSTR pwszAttr;
ULONG cAttr = 0;
ULONG cCount;
if ( ( pwszAttrList == NULL ) || ( wcslen( pwszAttrList ) == 0 ) )
{
pLdapUrlComponents->cAttr = 0;
pLdapUrlComponents->apwszAttr = NULL;
return( TRUE );
}
pwsz = new WCHAR [wcslen( pwszAttrList ) + 1];
if ( pwsz == NULL )
{
SetLastError( (DWORD) E_OUTOFMEMORY );
return( FALSE );
}
wcscpy( pwsz, pwszAttrList );
pwszAttr = wcstok( pwsz, L"," );
while ( pwszAttr != NULL )
{
cAttr += 1;
pwszAttr = wcstok( NULL, L"," );
}
pLdapUrlComponents->apwszAttr = new LPWSTR [cAttr+1];
if ( pLdapUrlComponents->apwszAttr == NULL )
{
delete [] pwsz;
SetLastError( (DWORD) E_OUTOFMEMORY );
return( FALSE );
}
pLdapUrlComponents->cAttr = cAttr;
for ( cCount = 0; cCount < cAttr; cCount++ )
{
pLdapUrlComponents->apwszAttr[cCount] = pwsz;
pwsz += ( wcslen(pwsz) + 1 );
}
pLdapUrlComponents->apwszAttr[cAttr] = NULL;
return( TRUE );
}
//+---------------------------------------------------------------------------
//
// Function: LdapParseCrackedScopeAndFilter
//
// Synopsis: Parse the cracked scope and filter
//
//----------------------------------------------------------------------------
BOOL
LdapParseCrackedScopeAndFilter (
LPWSTR pwszScope,
LPWSTR pwszFilter,
PLDAP_URL_COMPONENTS pLdapUrlComponents
)
{
ULONG Scope;
if ( _wcsicmp( pwszScope, L"base" ) == 0 )
{
Scope = LDAP_SCOPE_BASE;
}
else if ( _wcsicmp( pwszScope, L"one" ) == 0 )
{
Scope = LDAP_SCOPE_ONELEVEL;
}
else if ( _wcsicmp( pwszScope, L"sub" ) == 0 )
{
Scope = LDAP_SCOPE_SUBTREE;
}
else
{
SetLastError( (DWORD) E_INVALIDARG );
return( FALSE );
}
pLdapUrlComponents->pwszFilter = new WCHAR [wcslen( pwszFilter ) + 1];
if ( pLdapUrlComponents->pwszFilter == NULL )
{
SetLastError( (DWORD) E_OUTOFMEMORY );
return( FALSE );
}
wcscpy( pLdapUrlComponents->pwszFilter, pwszFilter );
pLdapUrlComponents->Scope = Scope;
return( TRUE );
}
//+---------------------------------------------------------------------------
//
// Function: LdapFreeUrlComponents
//
// Synopsis: Frees allocate URL components returned from LdapCrackUrl
//
//----------------------------------------------------------------------------
VOID
LdapFreeUrlComponents (
PLDAP_URL_COMPONENTS pLdapUrlComponents
)
{
delete [] pLdapUrlComponents->pwszHost;
delete [] pLdapUrlComponents->pwszDN;
if ( pLdapUrlComponents->apwszAttr != NULL )
{
delete pLdapUrlComponents->apwszAttr[0];
}
delete [] pLdapUrlComponents->apwszAttr;
delete [] pLdapUrlComponents->pwszFilter;
}
//+---------------------------------------------------------------------------
//
// Function: LdapGetBindings
//
// Synopsis: allocates and initializes the LDAP session binding
//
//----------------------------------------------------------------------------
BOOL
LdapGetBindings (
LPWSTR pwszHost,
ULONG Port,
DWORD dwRetrievalFlags,
DWORD dwBindFlags,
DWORD dwTimeout, // milliseconds
PCRYPT_CREDENTIALS pCredentials,
LDAP** ppld
)
{
BOOL fResult = TRUE;
DWORD LastError = 0;
CRYPT_PASSWORD_CREDENTIALSW PasswordCredentials;
LDAP* pld = NULL;
BOOL fFreeCredentials = FALSE;
memset( &PasswordCredentials, 0, sizeof( PasswordCredentials ) );
PasswordCredentials.cbSize = sizeof( PasswordCredentials );
if ( SchemeGetPasswordCredentialsW(
pCredentials,
&PasswordCredentials,
&fFreeCredentials
) == FALSE )
{
return( FALSE );
}
pld = ldap_initW( pwszHost, Port );
if ( pld != NULL )
{
SEC_WINNT_AUTH_IDENTITY_W AuthIdentity;
ULONG ldaperr;
struct l_timeval tv;
struct l_timeval *ptv = NULL;
if ((dwRetrievalFlags & CRYPT_LDAP_AREC_EXCLUSIVE_RETRIEVAL) &&
(NULL != pwszHost))
{
void *pvOn;
pvOn = LDAP_OPT_ON;
ldap_set_option(
pld,
LDAP_OPT_AREC_EXCLUSIVE,
&pvOn
);
}
// Note, dwTimeout is in units of milliseconds.
// LDAP_OPT_TIMELIMIT is in units of seconds.
if ( 0 != dwTimeout )
{
DWORD dwTimeoutSeconds = dwTimeout / 1000;
if ( LDAP_MIN_TIMEOUT_SECONDS > dwTimeoutSeconds )
{
dwTimeoutSeconds = LDAP_MIN_TIMEOUT_SECONDS;
}
tv.tv_sec = dwTimeoutSeconds;
tv.tv_usec = 0;
ptv = &tv;
ldap_set_option( pld, LDAP_OPT_TIMELIMIT,
(void *)&dwTimeoutSeconds );
}
ldaperr = ldap_connect( pld, ptv );
if ( ( ldaperr != LDAP_SUCCESS ) && ( pwszHost == NULL ) )
{
DWORD dwFlags = DS_FORCE_REDISCOVERY;
ULONG ldapsaveerr = ldaperr;
ldaperr = ldap_set_option(
pld,
LDAP_OPT_GETDSNAME_FLAGS,
(LPVOID)&dwFlags
);
if ( ldaperr == LDAP_SUCCESS )
{
ldaperr = ldap_connect( pld, ptv );
}
else
{
ldaperr = ldapsaveerr;
}
}
if ( ldaperr != LDAP_SUCCESS )
{
fResult = FALSE;
SetLastError( I_CryptNetLdapMapErrorToWin32( pld, ldaperr ) );
}
if ( fResult == TRUE )
{
fResult = SchemeGetAuthIdentityFromPasswordCredentialsW(
&PasswordCredentials,
&AuthIdentity
);
if ( fResult == TRUE )
{
#if 0
printf(
"Credentials = %S\\%S <%S>\n",
AuthIdentity.Domain,
AuthIdentity.User,
AuthIdentity.Password
);
#endif
fResult = LdapSSPIOrSimpleBind(
pld,
&AuthIdentity,
dwRetrievalFlags,
dwBindFlags
);
// following doesn't globber LastError
SchemeFreeAuthIdentityFromPasswordCredentialsW(
&PasswordCredentials,
&AuthIdentity
);
}
}
}
else
{
fResult = FALSE;
}
if ( fResult == TRUE )
{
*ppld = pld;
}
else
{
LastError = GetLastError();
LdapFreeBindings( pld );
}
if ( fFreeCredentials == TRUE )
{
SchemeFreePasswordCredentialsW( &PasswordCredentials );
}
SetLastError( LastError );
return( fResult );
}
//+---------------------------------------------------------------------------
//
// Function: LdapFreeBindings
//
// Synopsis: frees allocated LDAP session binding
//
//----------------------------------------------------------------------------
VOID
LdapFreeBindings (
LDAP* pld
)
{
if ( pld != NULL )
{
ldap_unbind_s( pld );
}
}
//+---------------------------------------------------------------------------
//
// Function: LdapSendReceiveUrlRequest
//
// Synopsis: sends an URL based search request to the LDAP server, receives
// the result message and converts it to a CRYPT_BLOB_ARRAY of
// encoded object bits
//
//----------------------------------------------------------------------------
BOOL
LdapSendReceiveUrlRequest (
LDAP* pld,
PLDAP_URL_COMPONENTS pLdapUrlComponents,
DWORD dwRetrievalFlags,
DWORD dwTimeout, // milliseconds
PCRYPT_BLOB_ARRAY pcba,
PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
{
BOOL fResult;
DWORD LastError = 0;
ULONG lderr;
LDAPMessage* plm = NULL;
if ( 0 != dwTimeout )
{
DWORD dwTimeoutSeconds = dwTimeout / 1000;
struct l_timeval tv;
if ( LDAP_MIN_TIMEOUT_SECONDS > dwTimeoutSeconds )
{
dwTimeoutSeconds = LDAP_MIN_TIMEOUT_SECONDS;
}
tv.tv_sec = dwTimeoutSeconds;
tv.tv_usec = 0;
lderr = ldap_search_stW(
pld,
pLdapUrlComponents->pwszDN,
pLdapUrlComponents->Scope,
pLdapUrlComponents->pwszFilter,
pLdapUrlComponents->apwszAttr,
FALSE,
&tv,
&plm
);
}
else
{
lderr = ldap_search_sW(
pld,
pLdapUrlComponents->pwszDN,
pLdapUrlComponents->Scope,
pLdapUrlComponents->pwszFilter,
pLdapUrlComponents->apwszAttr,
FALSE,
&plm
);
}
if ( lderr != LDAP_SUCCESS )
{
if ( plm != NULL )
{
ldap_msgfree( plm );
}
SetLastError( I_CryptNetLdapMapErrorToWin32( pld, lderr ) );
return( FALSE );
}
fResult = LdapConvertLdapResultMessage( pld, plm, dwRetrievalFlags, pcba, pAuxInfo);
if ( !fResult )
{
LastError = GetLastError();
}
ldap_msgfree( plm );
SetLastError( LastError );
return( fResult );
}
//+---------------------------------------------------------------------------
//
// Function: LdapConvertResultMessage
//
// Synopsis: convert returned LDAP message to a crypt blob array
//
//----------------------------------------------------------------------------
BOOL
LdapConvertLdapResultMessage (
LDAP* pld,
PLDAPMessage plm,
DWORD dwRetrievalFlags,
PCRYPT_BLOB_ARRAY pcba,
PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
{
BOOL fResult = TRUE;
PLDAPMessage plmElem;
BerElement* pber;
CHAR* pszAttr;
struct berval** apbv;
ULONG cCount;
CCryptBlobArray cba( 10, 5, fResult );
DWORD dwIndex;
ULONG cbIndex = 0;
char szIndex[33];
ULONG cbTotalVal = 0;
DWORD dwMaxUrlRetrievalByteCount = 0; // 0 => no max
if (pAuxInfo &&
offsetof(CRYPT_RETRIEVE_AUX_INFO, dwMaxUrlRetrievalByteCount) <
pAuxInfo->cbSize)
dwMaxUrlRetrievalByteCount = pAuxInfo->dwMaxUrlRetrievalByteCount;
for ( plmElem = ldap_first_entry( pld, plm ), dwIndex = 0;
( plmElem != NULL ) && ( fResult == TRUE );
plmElem = ldap_next_entry( pld, plmElem ), dwIndex++ )
{
if ( dwRetrievalFlags & CRYPT_LDAP_INSERT_ENTRY_ATTRIBUTE )
{
_ltoa(dwIndex, szIndex, 10);
cbIndex = strlen(szIndex) + 1;
}
for ( pszAttr = ldap_first_attributeA( pld, plmElem, &pber );
( pszAttr != NULL ) && ( fResult == TRUE );
pszAttr = ldap_next_attributeA( pld, plmElem, pber ) )
{
apbv = ldap_get_values_lenA( pld, plmElem, pszAttr );
if ( apbv == NULL )
{
fResult = FALSE;
}
for ( cCount = 0;
( fResult == TRUE ) && ( apbv[cCount] != NULL );
cCount++ )
{
ULONG cbAttr = 0;
ULONG cbVal;
ULONG cbToAdd;
LPBYTE pbToAdd;
cbToAdd = cbVal = apbv[cCount]->bv_len;
cbTotalVal += cbVal;
if ((0 != dwMaxUrlRetrievalByteCount) &&
(cbTotalVal > dwMaxUrlRetrievalByteCount))
{
I_CryptNetDebugErrorPrintfA(
"CRYPTNET.DLL --> Exceeded MaxUrlRetrievalByteCount for: Ldap Url\n");
SetLastError(ERROR_INVALID_DATA);
fResult = FALSE;
continue;
}
if ( dwRetrievalFlags & CRYPT_LDAP_INSERT_ENTRY_ATTRIBUTE )
{
cbAttr = strlen(pszAttr) + 1;
cbToAdd += cbIndex + cbAttr;
}
pbToAdd = cba.AllocBlob( cbToAdd );
if ( pbToAdd != NULL )
{
LPBYTE pb;
pb = pbToAdd;
if ( dwRetrievalFlags & CRYPT_LDAP_INSERT_ENTRY_ATTRIBUTE )
{
memcpy( pb, szIndex, cbIndex );
pb += cbIndex;
memcpy( pb, pszAttr, cbAttr );
pb += cbAttr;
}
memcpy( pb, (LPBYTE)apbv[cCount]->bv_val, cbVal );
}
else
{
SetLastError( (DWORD) E_OUTOFMEMORY );
fResult = FALSE;
}
if ( fResult == TRUE )
{
fResult = cba.AddBlob(
cbToAdd,
pbToAdd,
FALSE
);
if ( fResult == FALSE )
{
cba.FreeBlob( pbToAdd );
}
}
}
ldap_value_free_len( apbv );
}
}
if ( fResult == TRUE )
{
if ( cba.GetBlobCount() > 0 )
{
cba.GetArrayInNativeForm( pcba );
}
else
{
cba.FreeArray( TRUE );
SetLastError( (DWORD) CRYPT_E_NOT_FOUND );
fResult = FALSE;
}
}
else
{
cba.FreeArray( TRUE );
}
return( fResult );
}
//+---------------------------------------------------------------------------
//
// Function: LdapFreeCryptBlobArray
//
// Synopsis: free CRYPT_BLOB_ARRAY allocated in LdapConvertLdapResultMessage
//
//----------------------------------------------------------------------------
VOID
LdapFreeCryptBlobArray (
PCRYPT_BLOB_ARRAY pcba
)
{
CCryptBlobArray cba( pcba, 0 );
cba.FreeArray( TRUE );
}
//+---------------------------------------------------------------------------
//
// Function: LdapHasWriteAccess
//
// Synopsis: check if the caller has write access to the given LDAP URL
// query components
//
//----------------------------------------------------------------------------
BOOL
LdapHasWriteAccess (
LDAP* pld,
PLDAP_URL_COMPONENTS pLdapUrlComponents,
DWORD dwTimeout
)
{
BOOL fResult = FALSE;
LPWSTR pwszAttr = L"allowedAttributesEffective";
LPWSTR apwszAttr[2] = {pwszAttr, NULL};
LDAP_URL_COMPONENTS LdapUrlComponents;
CRYPT_BLOB_ARRAY cba;
ULONG cCount;
ULONG cchAttr;
LPSTR pszUrlAttr = NULL;
if ( ( pLdapUrlComponents->cAttr != 1 ) ||
( pLdapUrlComponents->Scope != LDAP_SCOPE_BASE ) )
{
return( FALSE );
}
memset( &LdapUrlComponents, 0, sizeof( LdapUrlComponents ) );
LdapUrlComponents.pwszHost = pLdapUrlComponents->pwszHost;
LdapUrlComponents.Port = pLdapUrlComponents->Port;
LdapUrlComponents.pwszDN = pLdapUrlComponents->pwszDN;
LdapUrlComponents.cAttr = 1;
LdapUrlComponents.apwszAttr = apwszAttr;
LdapUrlComponents.Scope = LDAP_SCOPE_BASE;
LdapUrlComponents.pwszFilter = L"(objectClass=*)";
if ( LdapSendReceiveUrlRequest( pld, &LdapUrlComponents, 0, dwTimeout, &cba, NULL ) == FALSE )
{
return( FALSE );
}
cchAttr = wcslen( pLdapUrlComponents->apwszAttr[ 0 ] );
pszUrlAttr = new CHAR [cchAttr + 1];
if ( pszUrlAttr == NULL )
{
SetLastError( (DWORD) E_OUTOFMEMORY );
return( FALSE );
}
if ( WideCharToMultiByte(
CP_ACP,
0,
pLdapUrlComponents->apwszAttr[ 0 ],
-1,
pszUrlAttr,
cchAttr + 1,
NULL,
NULL
) == FALSE )
{
delete [] pszUrlAttr;
return( FALSE );
}
for ( cCount = 0; cCount < cba.cBlob; cCount++ )
{
if ( cba.rgBlob[ cCount ].cbData != cchAttr )
{
continue;
}
if ( _strnicmp(
pszUrlAttr,
(LPSTR)cba.rgBlob[ cCount ].pbData,
cchAttr
) == 0 )
{
fResult = TRUE;
break;
}
}
LdapFreeCryptBlobArray( &cba );
delete [] pszUrlAttr;
return( fResult );
}
//+---------------------------------------------------------------------------
//
// Function: LdapSSPIOrSimpleBind
//
// Synopsis: do a SSPI and/or simple bind
//
//----------------------------------------------------------------------------
BOOL
LdapSSPIOrSimpleBind (
LDAP* pld,
SEC_WINNT_AUTH_IDENTITY_W* pAuthIdentity,
DWORD dwRetrievalFlags,
DWORD dwBindFlags
)
{
BOOL fResult = TRUE;
ULONG ldaperr;
ULONG uVersion= LDAP_VERSION3;
// Per bug 25497, do V3 negotiate instead of the default V2.
ldap_set_option(pld, LDAP_OPT_VERSION, &uVersion);
if (dwRetrievalFlags & CRYPT_LDAP_SIGN_RETRIEVAL)
{
void *pvOn;
pvOn = LDAP_OPT_ON;
ldaperr = ldap_set_option(
pld,
LDAP_OPT_SIGN,
&pvOn
);
if ( ldaperr != LDAP_SUCCESS )
{
SetLastError( I_CryptNetLdapMapErrorToWin32( pld, ldaperr ) );
return FALSE;
}
}
ldaperr = LDAP_AUTH_METHOD_NOT_SUPPORTED;
if (dwBindFlags & LDAP_BIND_AUTH_SSPI_ENABLE_FLAG)
{
ldaperr = ldap_bind_sW(
pld,
NULL,
(PWCHAR)pAuthIdentity,
LDAP_AUTH_SSPI
);
}
if (dwBindFlags & LDAP_BIND_AUTH_SIMPLE_ENABLE_FLAG)
{
// Per Anoop's 4/25/00 email:
// You should fall back to anonymous bind only if the server returns
// LDAP_AUTH_METHOD_NOT_SUPPORTED.
//
// Per sergiod/trevorf 4/25/01 also need to check for invalid creds
// because target server could be in a different forest.
if ( ldaperr == LDAP_AUTH_METHOD_NOT_SUPPORTED ||
ldaperr == LDAP_INVALID_CREDENTIALS )
{
ldaperr = ldap_bind_sW(
pld,
NULL,
NULL,
LDAP_AUTH_SIMPLE
);
if ( ldaperr != LDAP_SUCCESS )
{
uVersion = LDAP_VERSION2;
if ( LDAP_SUCCESS == ldap_set_option(pld,
LDAP_OPT_VERSION,
&uVersion) )
{
ldaperr = ldap_bind_sW(
pld,
NULL,
NULL,
LDAP_AUTH_SIMPLE
);
}
}
}
}
if ( ldaperr != LDAP_SUCCESS )
{
fResult = FALSE;
if ( ldaperr != LDAP_LOCAL_ERROR )
{
SetLastError( I_CryptNetLdapMapErrorToWin32( pld, ldaperr ) );
}
// else per Anoop's 4/25/00 email:
// For LDAP_LOCAL_ERROR, its an underlying security error where
// LastError has already been updated with a more meaningful error
// value.
}
return( fResult );
}
#if DBG
//+---------------------------------------------------------------------------
//
// Function: LdapDisplayUrlComponents
//
// Synopsis: display the URL components
//
//----------------------------------------------------------------------------
VOID
LdapDisplayUrlComponents (
PLDAP_URL_COMPONENTS pLdapUrlComponents
)
{
ULONG cCount;
printf( "pLdapUrlComponents->pwszHost = %S\n",
pLdapUrlComponents->pwszHost );
printf( "pLdapUrlComponents->Port = %d\n", pLdapUrlComponents->Port );
printf( "pLdapUrlComponents->pwszDN = %S\n", pLdapUrlComponents->pwszDN );
printf( "pLdapUrlComponents->cAttr = %d\n", pLdapUrlComponents->cAttr );
for ( cCount = 0; cCount < pLdapUrlComponents->cAttr; cCount++ )
{
printf(
"pLdapUrlComponents->apwszAttr[%d] = %S\n",
cCount,
pLdapUrlComponents->apwszAttr[cCount]
);
}
printf( "pLdapUrlComponents->Scope = %d\n", pLdapUrlComponents->Scope );
printf( "pLdapUrlComponents->pwszFilter = %S\n",
pLdapUrlComponents->pwszFilter );
}
#endif
DWORD g_dwLdapServerExtError;
CHAR g_rgszLdapServerError[128];
ULONG
I_CryptNetLdapMapErrorToWin32(
LDAP* pld,
ULONG LdapError
)
{
if (NULL != pld) {
CHAR *pszError = NULL;
DWORD dwError = ERROR_SUCCESS;
ULONG ldaperr;
ldaperr = ldap_get_option(pld, LDAP_OPT_SERVER_ERROR, &pszError);
if (LDAP_SUCCESS == ldaperr && NULL != pszError) {
DWORD cchBuf = sizeof(g_rgszLdapServerError) /
sizeof(g_rgszLdapServerError[0]);
strncpy(g_rgszLdapServerError, pszError, cchBuf - 1);
g_rgszLdapServerError[cchBuf - 1] = '\0';
}
ldaperr = ldap_get_option(pld, LDAP_OPT_SERVER_EXT_ERROR, &dwError);
if (LDAP_SUCCESS == ldaperr)
g_dwLdapServerExtError = dwError;
I_CryptNetDebugErrorPrintfA("CRYPTNET.DLL --> LdapError: 0x%x <%s>\n",
LdapError, pszError);
if (NULL != pszError)
ldap_memfreeA(pszError);
}
return LdapMapErrorToWin32(LdapError);
}