//+--------------------------------------------------------------------------- // // 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 #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); }