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