|
|
//---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1995
//
// File: cuar.cxx
//
// Contents: Account Restrictions Propset for the User object
//
// History: 11-1-95 krishnag Created.
//
// PROPERTY_RW(AccountDisabled, boolean, 1) I
// PROPERTY_RW(AccountExpirationDate, DATE, 2) I
// PROPERTY_RO(AccountCanExpire, boolean, 3) I
// PROPERTY_RO(PasswordCanExpire, boolean, 4) I
// PROPERTY_RW(GraceLoginsAllowed, long, 5) NI
// PROPERTY_RW(GraceLoginsRemaining, long, 6) NI
// PROPERTY_RW(IsAccountLocked, boolean, 7) I
// PROPERTY_RW(IsAdmin, boolean, 8) I
// PROPERTY_RW(LoginHours, VARIANT, 9) I
// PROPERTY_RW(LoginWorkstations, VARIANT, 10) I
// PROPERTY_RW(MaxLogins, long, 11) I
// PROPERTY_RW(MaxStorage, long, 12) I
// PROPERTY_RW(PasswordExpirationDate, DATE, 13) I
// PROPERTY_RW(PasswordRequired, boolean, 14) I
// PROPERTY_RW(RequireUniquePassword,boolean, 15) I
//
//
//----------------------------------------------------------------------------
#include "ldap.hxx"
#pragma hdrstop
#include <lm.h>
#include <winldap.h>
#include "..\ldapc\ldpcache.hxx"
#include "..\ldapc\ldaputil.hxx"
#include "..\ldapc\parse.hxx"
#include <dsgetdc.h>
#include <sspi.h>
HRESULT BuildLDAPPathFromADsPath2( LPWSTR szADsPathName, LPWSTR *pszLDAPServer, LPWSTR *pszLDAPDn, DWORD * pdwPort );
DWORD GetDefaultServer( DWORD dwPort, BOOL fVerify, LPWSTR szDomainDnsName, LPWSTR szServerName, BOOL fWriteable );
HRESULT GetDomainDNSNameFromHost( LPWSTR szHostName, SEC_WINNT_AUTH_IDENTITY& AuthI, CCredentials &Credentials, DWORD dwPort, LPWSTR * ppszHostName );
//
// The list of server entries - detailing SSL support
//
PSERVSSLENTRY gpServerSSLList = NULL;
//
// Critical Section and support routines to protect list
//
CRITICAL_SECTION g_ServerListCritSect;
//
// Flag that indicates if kerberos is being used.
//
const unsigned long KERB_SUPPORT_FLAGS = ISC_RET_MUTUAL_AUTH ; //
// Routines that support cacheing server SSL info for perf
//
#define STRING_LENGTH(p) ( p ? wcslen(p) : 0)
//
// Get the status of SSL support on the server pszServerName
// 0 indicates that the server was not in our list.
//
DWORD ReadServerSupportsSSL( LPWSTR pszServerName) { ENTER_SERVERLIST_CRITICAL_SECTION(); PSERVSSLENTRY pServerList = gpServerSSLList; DWORD dwRetVal = 0;
//
// Keep going through the list until we hit the end or
// we find an entry that matches.
//
while ((pServerList != NULL) && (dwRetVal == 0)) {
#ifdef WIN95
if (!(_wcsicmp(pszServerName, pServerList->pszServerName))) { #else
if (CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, pszServerName, -1, pServerList->pszServerName, -1 ) == CSTR_EQUAL ) { #endif
dwRetVal = pServerList->dwFlags; }
pServerList = pServerList->pNext; }
LEAVE_SERVERLIST_CRITICAL_SECTION();
return dwRetVal; }
HRESULT UpdateServerSSLSupportStatus( PWSTR pszServerName, DWORD dwFlags ) { HRESULT hr = S_OK; PSERVSSLENTRY pServEntry = NULL; ENTER_SERVERLIST_CRITICAL_SECTION() PSERVSSLENTRY pServerList = gpServerSSLList; DWORD dwRetVal = 0;
ADsAssert(pszServerName && *pszServerName);
//
// Keep going through the list until we hit the end or
// we find an entry that matches.
//
while ((pServerList != NULL) && (dwRetVal == 0)) {
#ifdef WIN95
if (!(_wcsicmp(pszServerName, pServerList->pszServerName))) { #else
if (CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, pszServerName, -1, pServerList->pszServerName, -1 ) == CSTR_EQUAL ) { #endif
pServerList->dwFlags = dwFlags; LEAVE_SERVERLIST_CRITICAL_SECTION() RRETURN(S_OK); }
pServerList = pServerList->pNext; }
pServEntry = (PSERVSSLENTRY) AllocADsMem(sizeof(SERVSSLENTRY));
if (!pServEntry) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); }
pServEntry->pszServerName = AllocADsStr(pszServerName); if (!pServEntry->pszServerName) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); }
pServEntry->dwFlags = dwFlags;
pServEntry->pNext = gpServerSSLList; gpServerSSLList = pServEntry;
error: if (FAILED(hr) && pServEntry) { //
// Free only pServEntry as the string cannot have
// a value in the error case
//
FreeADsMem(pServEntry); }
LEAVE_SERVERLIST_CRITICAL_SECTION();
RRETURN(hr); }
void FreeServerSSLSupportList() { PSERVSSLENTRY pList = gpServerSSLList; PSERVSSLENTRY pPrevEntry = NULL;
while (pList) { pPrevEntry = pList;
FreeADsStr(pList->pszServerName); pList = pList->pNext;
FreeADsMem(pPrevEntry); } }
#if (!defined(WIN95))
//
// Take a AuthI struct and return a cred handle.
//
HRESULT ConvertAuthIdentityToCredHandle( SEC_WINNT_AUTH_IDENTITY& AuthI, OUT PCredHandle CredentialsHandle ) { SECURITY_STATUS secStatus = SEC_E_OK; TimeStamp Lifetime;
secStatus = AcquireCredentialsHandleWrapper( NULL, // New principal
MICROSOFT_KERBEROS_NAME_W, SECPKG_CRED_OUTBOUND, NULL, &AuthI, NULL, NULL, CredentialsHandle, &Lifetime );
if (secStatus != SEC_E_OK) {
RRETURN(E_FAIL); } else { RRETURN(S_OK); }
}
//
// ***** Caller must free the strings put in the ****
// ***** AuthIdentity struct later. ****
//
HRESULT GetAuthIdentityForCaller( CCredentials& Credentials, IADs * pIADs, OUT SEC_WINNT_AUTH_IDENTITY *pAuthI, BOOL fEnforceMutualAuth ) { HRESULT hr = S_OK; LPWSTR pszNTLMUser = NULL; LPWSTR pszNTLMDomain = NULL; LPWSTR pszDefaultServer = NULL; LPWSTR dn = NULL; LPWSTR passwd = NULL; IADsObjOptPrivate * pADsPrivObjectOptions = NULL; ULONG ulFlags = 0;
if (fEnforceMutualAuth) {
hr = pIADs->QueryInterface( IID_IADsObjOptPrivate, (void **)&pADsPrivObjectOptions ); BAIL_ON_FAILURE(hr);
hr = pADsPrivObjectOptions->GetOption ( LDAP_MUTUAL_AUTH_STATUS, (void *) &ulFlags );
BAIL_ON_FAILURE(hr);
if (!(ulFlags & KERB_SUPPORT_FLAGS)) { BAIL_ON_FAILURE(hr = E_FAIL); } }
hr = Credentials.GetUserName(&dn); BAIL_ON_FAILURE(hr);
hr = Credentials.GetPassword(&passwd); BAIL_ON_FAILURE(hr);
//
// Get the userName and password into the auth struct.
//
hr = LdapCrackUserDNtoNTLMUser2( dn, &pszNTLMUser, &pszNTLMDomain );
if (FAILED(hr)) { hr = LdapCrackUserDNtoNTLMUser( dn, &pszNTLMUser, &pszNTLMDomain ); BAIL_ON_FAILURE(hr); }
//
// If the domain name is NULL and enforceMutualAuth is false,
// then we need to throw in the defaultDomainName. This will
// be needed subsequently for the LogonUser call.
//
if (!fEnforceMutualAuth && !pszNTLMDomain) { //
// Call GetDefaultServer.
//
pszDefaultServer = (LPWSTR) AllocADsMem(sizeof(WCHAR) * MAX_PATH); if (!pszDefaultServer) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); }
pszNTLMDomain = (LPWSTR) AllocADsMem(sizeof(WCHAR) * MAX_PATH); if (!pszNTLMDomain) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); }
hr = GetDefaultServer( -1, // this will use the default ldap port
FALSE, pszNTLMDomain, pszDefaultServer, TRUE ); BAIL_ON_FAILURE(hr); }
pAuthI->User = (PWCHAR)pszNTLMUser; pAuthI->UserLength = (pszNTLMUser == NULL)? 0: wcslen(pszNTLMUser); pAuthI->Domain = (PWCHAR)pszNTLMDomain; pAuthI->DomainLength = (pszNTLMDomain == NULL)? 0: wcslen(pszNTLMDomain); pAuthI->Password = (PWCHAR)passwd; pAuthI->PasswordLength = (passwd == NULL)? 0: wcslen(passwd); pAuthI->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
error:
if (FAILED(hr)) {
//
// Free the strings
//
if (pszNTLMUser) { FreeADsStr(pszNTLMUser); }
if (pszNTLMDomain) { FreeADsStr(pszNTLMDomain); }
if (passwd) { FreeADsStr(passwd); } }
if (pADsPrivObjectOptions) { pADsPrivObjectOptions->Release(); }
//
// Always free the dn
//
if (dn) { FreeADsStr(dn); }
if (pszDefaultServer) { FreeADsMem(pszDefaultServer); }
RRETURN(hr); } #endif
// Class CLDAPUser
STDMETHODIMP CLDAPUser::get_AccountDisabled(THIS_ VARIANT_BOOL FAR* retval) { if ( retval == NULL ) RRETURN( E_ADS_BAD_PARAMETER );
LONG lUserAcctControl; HRESULT hr = get_LONG_Property( (IADsUser *)this, TEXT("userAccountControl"), &lUserAcctControl );
if ( SUCCEEDED(hr)) *retval = lUserAcctControl & UF_ACCOUNTDISABLE ? VARIANT_TRUE : VARIANT_FALSE;
RRETURN(hr); }
STDMETHODIMP CLDAPUser::put_AccountDisabled(THIS_ VARIANT_BOOL fAccountDisabled) { LONG lUserAcctControl; HRESULT hr = get_LONG_Property( (IADsUser *)this, TEXT("userAccountControl"), &lUserAcctControl ); if ( SUCCEEDED(hr)) { if ( fAccountDisabled ) lUserAcctControl |= UF_ACCOUNTDISABLE; else lUserAcctControl &= ~UF_ACCOUNTDISABLE;
hr = put_LONG_Property( (IADsUser *)this, TEXT("userAccountControl"), lUserAcctControl ); }
RRETURN(hr); }
STDMETHODIMP CLDAPUser::get_AccountExpirationDate(THIS_ DATE FAR* retval) { GET_PROPERTY_FILETIME((IADsUser *)this, AccountExpirationDate); }
STDMETHODIMP CLDAPUser::put_AccountExpirationDate(THIS_ DATE daAccountExpirationDate) { PUT_PROPERTY_FILETIME((IADsUser *)this, AccountExpirationDate); }
STDMETHODIMP CLDAPUser::get_GraceLoginsAllowed(THIS_ long FAR* retval) { RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED); }
STDMETHODIMP CLDAPUser::put_GraceLoginsAllowed(THIS_ long lGraceLoginsAllowed) { RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED); }
STDMETHODIMP CLDAPUser::get_GraceLoginsRemaining(THIS_ long FAR* retval) { RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED); }
STDMETHODIMP CLDAPUser::put_GraceLoginsRemaining(THIS_ long lGraceLoginsRemaining) { RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED); }
STDMETHODIMP CLDAPUser::get_IsAccountLocked(THIS_ VARIANT_BOOL FAR* retval) { HRESULT hr = S_OK;
VARIANT var; IADsLargeInteger *pLargeInt = NULL;
LONG LowPart, HighPart;
if ( retval == NULL ) RRETURN( E_ADS_BAD_PARAMETER );
VariantInit(&var);
hr = _pADs->Get(TEXT("lockoutTime"), &var);
if (SUCCEEDED(hr)) { //
// There's a lockoutTime, we need to determine
// if it equals 0 (== not locked-out).
//
ADsAssert(V_VT(&var) == VT_DISPATCH);
if (V_VT(&var) != VT_DISPATCH) { BAIL_ON_FAILURE(hr = E_FAIL); }
hr = V_DISPATCH(&var)->QueryInterface(IID_IADsLargeInteger, reinterpret_cast<void**>(&pLargeInt) ); BAIL_ON_FAILURE(hr);
hr = pLargeInt->get_LowPart(&LowPart); BAIL_ON_FAILURE(hr); hr = pLargeInt->get_HighPart(&HighPart); BAIL_ON_FAILURE(hr);
if ( (LowPart != 0) || (HighPart != 0) ) { *retval = VARIANT_TRUE; } else { *retval = VARIANT_FALSE; }
} else if (hr == E_ADS_PROPERTY_NOT_FOUND) { //
// If there's no lockoutTime, the account is not
// locked-out.
//
*retval = VARIANT_FALSE; hr = S_OK; } else { BAIL_ON_FAILURE(hr); }
error:
if (pLargeInt) { pLargeInt->Release(); }
VariantClear(&var); RRETURN(hr); }
STDMETHODIMP CLDAPUser::put_IsAccountLocked(THIS_ VARIANT_BOOL fIsAccountLocked) { HRESULT hr = S_OK;
if (fIsAccountLocked) { //
// You cannot set an account to a locked state.
//
RRETURN(E_ADS_BAD_PARAMETER); }
hr = put_LONG_Property( (IADsUser *)this, TEXT("lockoutTime"), 0 );
RRETURN(hr); }
STDMETHODIMP CLDAPUser::get_LoginHours(THIS_ VARIANT FAR* retval) { GET_PROPERTY_VARIANT((IADsUser *)this, LoginHours); }
STDMETHODIMP CLDAPUser::put_LoginHours(THIS_ VARIANT vLoginHours) { PUT_PROPERTY_VARIANT((IADsUser *)this, LoginHours); }
STDMETHODIMP CLDAPUser::get_LoginWorkstations(THIS_ VARIANT FAR* retval) { GET_PROPERTY_BSTRARRAY((IADsUser *)this,LoginWorkstations); }
STDMETHODIMP CLDAPUser::put_LoginWorkstations(THIS_ VARIANT vLoginWorkstations) { PUT_PROPERTY_BSTRARRAY((IADsUser *)this,LoginWorkstations); }
STDMETHODIMP CLDAPUser::get_MaxLogins(THIS_ long FAR* retval) { RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED); }
STDMETHODIMP CLDAPUser::put_MaxLogins(THIS_ long lMaxLogins) { RRETURN(E_ADS_PROPERTY_NOT_SUPPORTED); }
STDMETHODIMP CLDAPUser::get_MaxStorage(THIS_ long FAR* retval) { GET_PROPERTY_LONG((IADsUser *)this, MaxStorage); }
STDMETHODIMP CLDAPUser::put_MaxStorage(THIS_ long lMaxStorage) { PUT_PROPERTY_LONG((IADsUser *)this, MaxStorage); }
STDMETHODIMP CLDAPUser::get_PasswordExpirationDate(THIS_ DATE FAR* retval) { GET_PROPERTY_DATE((IADsUser *)this, PasswordExpirationDate); }
STDMETHODIMP CLDAPUser::put_PasswordExpirationDate(THIS_ DATE daPasswordExpirationDate) { PUT_PROPERTY_DATE((IADsUser *)this, PasswordExpirationDate); }
STDMETHODIMP CLDAPUser::get_PasswordRequired(THIS_ VARIANT_BOOL FAR* retval) { if ( retval == NULL ) RRETURN( E_ADS_BAD_PARAMETER );
LONG lUserAcctControl; HRESULT hr = get_LONG_Property( (IADsUser *)this, TEXT("userAccountControl"), &lUserAcctControl );
if ( SUCCEEDED(hr)) *retval = lUserAcctControl & UF_PASSWD_NOTREQD ? VARIANT_FALSE: VARIANT_TRUE;
RRETURN(hr); }
STDMETHODIMP CLDAPUser::put_PasswordRequired(THIS_ VARIANT_BOOL fPasswordRequired) { LONG lUserAcctControl; HRESULT hr = get_LONG_Property( (IADsUser *)this, TEXT("userAccountControl"), &lUserAcctControl ); if ( SUCCEEDED(hr)) { if ( fPasswordRequired ) lUserAcctControl &= ~UF_PASSWD_NOTREQD; else lUserAcctControl |= UF_PASSWD_NOTREQD;
hr = put_LONG_Property( (IADsUser *)this, TEXT("userAccountControl"), lUserAcctControl ); }
RRETURN(hr); }
STDMETHODIMP CLDAPUser::get_PasswordMinimumLength(THIS_ LONG FAR* retval) { GET_PROPERTY_LONG((IADsUser *)this, PasswordMinimumLength); }
STDMETHODIMP CLDAPUser::put_PasswordMinimumLength(THIS_ LONG lPasswordMinimumLength) { PUT_PROPERTY_LONG((IADsUser *)this, PasswordMinimumLength); }
STDMETHODIMP CLDAPUser::get_RequireUniquePassword(THIS_ VARIANT_BOOL FAR* retval) { GET_PROPERTY_VARIANT_BOOL((IADsUser *)this, RequireUniquePassword); }
STDMETHODIMP CLDAPUser::put_RequireUniquePassword(THIS_ VARIANT_BOOL fRequireUniquePassword) { PUT_PROPERTY_VARIANT_BOOL((IADsUser *)this, RequireUniquePassword); }
BOOLEAN _cdecl ServerCertCallback( PLDAP Connection, PCCERT_CONTEXT pServerCert ) { //
// After the secure connection is established, this function is called by
// LDAP. This gives the client an opportunity to verify the server cert.
// If, for some reason, the client doesn't approve it, it should return FALSE
// and the connection will be terminated. Else, return TRUE
//
fprintf( stderr, "Server cert callback has been called...\n" );
//
// Use some way to verify the server certificate.
//
return TRUE; }
STDMETHODIMP CLDAPUser::SetPassword(THIS_ BSTR bstrNewPassword) { HRESULT hr = E_FAIL; BOOLEAN bUseLDAP = FALSE;
LPWSTR pszServer = NULL; LPWSTR pszHostName = NULL; DWORD dwLen = 0; int err = 0;
BSTR bstrADsPath = NULL; LPWSTR szServerSSL = NULL; LPWSTR szDn = NULL; DWORD dwPortSSL = 0; PADSLDP pAdsLdpSSL = NULL;
IADsObjOptPrivate * pADsPrivObjectOptions = NULL; PADSLDP pAdsLdp = NULL; LDAPMessage *pMsgResult = NULL; LDAPMessage *pMsgEntry = NULL; LDAP *pLdapCurrent = NULL; LPWSTR Attributes[] = {L"objectClass", NULL};
VARIANT varSamAccount; DWORD dwServerPwdSupport = SERVER_STATUS_UNKNOWN; LPWSTR pszHostDomainName = NULL; SEC_WINNT_AUTH_IDENTITY AuthI; BOOLEAN fPasswordSet = FALSE; LPWSTR pszTempPwd = NULL; ULONG ulFlags = 0; VARIANT varGetInfoEx; BOOL fCachePrimed = FALSE;
BOOL fImpersonating = FALSE; HANDLE hUserToken = INVALID_HANDLE_VALUE;
//
// Init params we will need to free later.
//
AuthI.User = NULL; AuthI.Domain = NULL; AuthI.Password = NULL; VariantInit(&varSamAccount); VariantInit(&varGetInfoEx);
//
// Get the Ldap path of the user object
//
hr = _pADs->get_ADsPath( &bstrADsPath ); BAIL_ON_FAILURE(hr);
hr = BuildLDAPPathFromADsPath2( bstrADsPath, &szServerSSL, &szDn, &dwPortSSL ); BAIL_ON_FAILURE(hr);
//
// Now do an LDAP Search with Referrals and get the handle to success
// connection. This is where we can find the server the referred object
// resides on
//
hr = _pADs->QueryInterface( IID_IADsObjOptPrivate, (void **)&pADsPrivObjectOptions ); BAIL_ON_FAILURE(hr);
hr = pADsPrivObjectOptions->GetOption ( LDAP_SERVER, (void*)&pszHostName );
BAIL_ON_FAILURE(hr);
//
// additional lengh 3 is for '\0' and "\\\\"
//
dwLen = STRING_LENGTH(pszHostName) + 3; pszServer = (LPWSTR) AllocADsMem( dwLen * sizeof(WCHAR) ); if (!pszServer) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } wcscpy(pszServer,L"\\\\"); wcscat(pszServer, pszHostName); dwServerPwdSupport = ReadServerSupportsSSL(pszHostName);
if (dwServerPwdSupport == ( SERVER_STATUS_UNKNOWN | SERVER_DOES_NOT_SUPPORT_SSL | SERVER_DOES_NOT_SUPPORT_NETUSER | SERVER_DOES_NOT_SUPPORT_KERBEROS ) ) { //
// All flags are set, we will reset and rebuild cache
//
UpdateServerSSLSupportStatus( pszHostName, SERVER_STATUS_UNKNOWN ); dwServerPwdSupport = SERVER_STATUS_UNKNOWN; }
if (dwServerPwdSupport == SERVER_STATUS_UNKNOWN || !(dwServerPwdSupport & SERVER_DOES_NOT_SUPPORT_SSL)) {
//
// Try to establish SSL connection for this Password Operation
//
hr = LdapOpenObject( pszHostName, szDn, &pAdsLdpSSL, _Credentials, 636 );
if (SUCCEEDED(hr)) { int retval; SecPkgContext_ConnectionInfo sslattr;
retval = ldap_get_option( pAdsLdpSSL->LdapHandle, LDAP_OPT_SSL_INFO, &sslattr ); if (retval == LDAP_SUCCESS) { //
// If Channel is secure enough, enable LDAP Password Change
//
if (sslattr.dwCipherStrength >= 128) { bUseLDAP = TRUE; } } }
//
// Update the SSL support if appropriate
//
if (dwServerPwdSupport == SERVER_STATUS_UNKNOWN || !bUseLDAP) {
//
// Set the server does not support ssl bit if necessary
//
UpdateServerSSLSupportStatus( pszHostName, bUseLDAP ? dwServerPwdSupport : dwServerPwdSupport |= SERVER_DOES_NOT_SUPPORT_SSL ); } }
if (bUseLDAP) { //
// LDAP Password Set
//
PLDAPModW prgMod[2]; LDAPModW ModReplace; struct berval* rgBerVal[2]; struct berval BerVal; int ipwdLen;
prgMod[0] = &ModReplace; prgMod[1] = NULL;
ModReplace.mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES; ModReplace.mod_type = L"unicodePwd"; ModReplace.mod_bvalues = rgBerVal; rgBerVal[0] = &BerVal; rgBerVal[1] = NULL;
//
// 2 extra for "" to put the password in.
//
if (bstrNewPassword) { ipwdLen = (wcslen(bstrNewPassword) + 2) * sizeof(WCHAR); } else { ipwdLen = 2 * sizeof(WCHAR); }
//
// Add 1 for the \0.
//
pszTempPwd = (LPWSTR) AllocADsMem(ipwdLen + sizeof(WCHAR)); if (!pszTempPwd) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); }
wcscpy(pszTempPwd, L"\""); if (bstrNewPassword) { wcscat(pszTempPwd, bstrNewPassword); }
wcscat(pszTempPwd, L"\"");
BerVal.bv_len = ipwdLen; BerVal.bv_val = (char*)pszTempPwd;
hr = LdapModifyS( pAdsLdpSSL, szDn, prgMod ); BAIL_ON_FAILURE(hr);
//
// Set flag so that we do not try any other methods.
//
fPasswordSet = TRUE; }
//
// Try kerberos setpassword if applicable
//
#if (!defined(WIN95))
//
// Only valid on Win2k
//
if (!fPasswordSet) {
//
// If we cached the server as not supporting Kerberos, most likely it
// was because we were not mutually authenticated. Do a quick check to
// see if that has changed, so that we can update our cached information
// if necessary.
//
if (dwServerPwdSupport & SERVER_DOES_NOT_SUPPORT_KERBEROS) {
hr = pADsPrivObjectOptions->GetOption ( LDAP_MUTUAL_AUTH_STATUS, (void *) &ulFlags );
BAIL_ON_FAILURE(hr);
if ((ulFlags & KERB_SUPPORT_FLAGS)) { UpdateServerSSLSupportStatus( pszHostName, dwServerPwdSupport &= (~SERVER_DOES_NOT_SUPPORT_KERBEROS) ); } } if (!(dwServerPwdSupport & SERVER_DOES_NOT_SUPPORT_KERBEROS)) {
//
// Kerberos set password
//
CredHandle secCredHandle = {0}; SECURITY_STATUS SecStatus; DWORD dwStatus = 0; LPWSTR pszSamAccountArr[] = {L"sAMAccountName"};
if (!fCachePrimed) { hr = ADsBuildVarArrayStr( pszSamAccountArr, 1, &varGetInfoEx ); BAIL_ON_FAILURE(hr);
hr = _pADs->GetInfoEx(varGetInfoEx, 0); BAIL_ON_FAILURE(hr);
fCachePrimed = TRUE; } hr = _pADs->Get(L"sAMAccountName", &varSamAccount); BAIL_ON_FAILURE(hr);
//
// The AuthIdentity structure is ueful down the road.
// This routine will fail if we were not bound using
// kerberos to the server.
//
hr = GetAuthIdentityForCaller( _Credentials, _pADs, &AuthI, TRUE // enforce mutual auth.
);
if (FAILED(hr)) { UpdateServerSSLSupportStatus( pszHostName, dwServerPwdSupport |= SERVER_DOES_NOT_SUPPORT_KERBEROS ); } else {
//
// Kerb really needs this handle.
//
hr = ConvertAuthIdentityToCredHandle( AuthI, &secCredHandle );
if (FAILED(hr)) { UpdateServerSSLSupportStatus( pszHostName, dwServerPwdSupport |= SERVER_DOES_NOT_SUPPORT_KERBEROS ); }
if (SUCCEEDED(hr)) {
//
// Get the domain dns name for the user
//
hr = GetDomainDNSNameFromHost( pszHostName, AuthI, _Credentials, dwPortSSL, &pszHostDomainName );
if (SUCCEEDED(hr)) {
dwStatus = KerbSetPasswordUserEx( pszHostDomainName, V_BSTR(&varSamAccount), bstrNewPassword, &secCredHandle, pszHostName );
if (dwStatus) { //
// We should have got this to come in here.
//
hr = HRESULT_FROM_WIN32(ERROR_LOGON_FAILURE); } else { fPasswordSet = TRUE; } } // if domain dns name get succeeded.
FreeCredentialsHandleWrapper(&secCredHandle);
} // if GetCredentialsForCaller succeeded.
} // if we could get authidentity succesfully
} // if server supports kerberos
} // if password not set.
#endif
//
// At this point server status cannot be unknown, it
// will atleast have info about ssl support.
//
if (!fPasswordSet) {
if (!(dwServerPwdSupport & SERVER_DOES_NOT_SUPPORT_NETUSER)) { //
// Password Set using NET APIs
//
NET_API_STATUS nasStatus; DWORD dwParmErr = 0; LPWSTR pszSamAccountArr[] = {L"sAMAccountName"};
//
// Get SamAccountName
//
VariantClear(&varSamAccount); VariantClear(&varGetInfoEx);
if (!fCachePrimed) { hr = ADsBuildVarArrayStr( pszSamAccountArr, 1, &varGetInfoEx ); BAIL_ON_FAILURE(hr);
hr = _pADs->GetInfoEx(varGetInfoEx, 0); BAIL_ON_FAILURE(hr);
fCachePrimed = TRUE; }
hr = _pADs->Get(L"sAMAccountName", &varSamAccount); BAIL_ON_FAILURE(hr);
//
// Set the password
//
USER_INFO_1003 lpUserInfo1003 ;
lpUserInfo1003.usri1003_password = bstrNewPassword;
#ifndef Win95
//
// At this point if the user credentials are non NULL,
// we want to impersonate the user and then make this call.
// This will make sure the NetUserSetInfo call is made in the
// correct context.
//
if (!_Credentials.IsNullCredentials()) { //
// Need to get the userName and password in the format
// usable by the logonUser call.
//
if ((AuthI.User == NULL) && (AuthI.Domain == NULL) && (AuthI.Password == NULL) ) { //
// Get teh Auth identity struct populate if necessary.
//
hr = GetAuthIdentityForCaller( _Credentials, _pADs, &AuthI, FALSE ); }
BAIL_ON_FAILURE(hr);
//
// Note that if this code is backported, then we might
// need to change LOGON32_PROVIDER_WINNT50 to
// LOGON32_PROVIDER_DEFAULT as NT4 and below will support
// only that option. Also note that Win2k and below, do not
// allow all accounts to impersonate.
//
if (LogonUser( AuthI.User, AuthI.Domain, AuthI.Password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50, &hUserToken ) ) { //
// Call succeeded so we should use this context.
//
if (ImpersonateLoggedOnUser(hUserToken)) { fImpersonating = TRUE; } } if (!fImpersonating) { hr = HRESULT_FROM_WIN32(GetLastError()); } BAIL_ON_FAILURE(hr); } // if credentials are valid.
#endif
nasStatus = NetUserSetInfo( pszServer, V_BSTR(&varSamAccount), 1003, (LPBYTE)&lpUserInfo1003, &dwParmErr ); #ifndef Win95
if (fImpersonating) { if (RevertToSelf()) { fImpersonating = FALSE; } else { ADsAssert(!"Revert to self failed."); BAIL_ON_FAILURE(hr = HRESULT_FROM_WIN32(GetLastError())); } } #endif
if ( nasStatus == NERR_UserNotFound ) { // User not created yet
hr = E_ADS_OBJECT_UNBOUND; BAIL_ON_FAILURE(hr); }
hr = HRESULT_FROM_WIN32(nasStatus); if (FAILED(hr) && (nasStatus == ERROR_LOGON_FAILURE)) {
//
// Was failure and ERROR_LOGON_FAILURE
//
UpdateServerSSLSupportStatus( pszHostName, dwServerPwdSupport |= SERVER_DOES_NOT_SUPPORT_NETUSER ); //
// Need to free the variant as we will re-read in kerb
//
VariantClear(&varSamAccount); } else { //
// password set succeed
//
fPasswordSet = TRUE; } } } // if Password not set.
error: if (bstrADsPath) { ADsFreeString(bstrADsPath); }
if (szServerSSL) { FreeADsStr(szServerSSL); }
if (szDn) { FreeADsStr(szDn); }
if (pAdsLdpSSL) { LdapCloseObject(pAdsLdpSSL); }
if (pADsPrivObjectOptions) { pADsPrivObjectOptions->Release(); }
if (pMsgResult) { LdapMsgFree(pMsgResult); }
if (pszHostDomainName) { FreeADsStr(pszHostDomainName); }
if (AuthI.User) { FreeADsStr(AuthI.User); }
if (AuthI.Domain) { FreeADsStr(AuthI.Domain); }
if (AuthI.Password) { FreeADsStr(AuthI.Password); }
if (pszTempPwd) { FreeADsMem(pszTempPwd); }
if (pszHostName) { FreeADsStr(pszHostName); }
if (pszServer) { FreeADsMem(pszServer); }
#ifndef Win95
if (fImpersonating) { //
// Try and call revert to self again
//
RevertToSelf(); } #endif
if (hUserToken != INVALID_HANDLE_VALUE ) { CloseHandle(hUserToken); hUserToken = NULL; }
VariantClear(&varSamAccount); VariantClear(&varGetInfoEx);
RRETURN(hr); }
STDMETHODIMP CLDAPUser::ChangePassword(THIS_ BSTR bstrOldPassword, BSTR bstrNewPassword) { HRESULT hr = S_OK; BOOLEAN bUseLDAP = FALSE;
LPWSTR pszServer = NULL; LPWSTR pszHostName = NULL; DWORD dwLen = 0; int err = 0;
BSTR bstrADsPath = NULL; LPWSTR szServerSSL = NULL; LPWSTR szDn = NULL; DWORD dwPortSSL = 0; PADSLDP pAdsLdpSSL = NULL;
IADsObjOptPrivate * pADsPrivObjectOptions = NULL; PADSLDP pAdsLdp = NULL; LDAPMessage *pMsgResult = NULL; LDAPMessage *pMsgEntry = NULL; LDAP *pLdapCurrent = NULL; LPWSTR Attributes[] = {L"objectClass", NULL};
VARIANT varSamAccount; DWORD dwServerSSLSupport = 0; LPWSTR pszNewPassword = NULL; LPWSTR pszOldPassword = NULL; VARIANT varGetInfoEx; SEC_WINNT_AUTH_IDENTITY AuthI; BOOL fImpersonating = FALSE; HANDLE hUserToken = INVALID_HANDLE_VALUE;
VariantInit(&varSamAccount); VariantInit(&varGetInfoEx); memset(&AuthI, 0, sizeof(SEC_WINNT_AUTH_IDENTITY));
//
// Get the Ldap path of the user object
//
hr = _pADs->get_ADsPath( &bstrADsPath ); BAIL_ON_FAILURE(hr);
hr = BuildLDAPPathFromADsPath2( bstrADsPath, &szServerSSL, &szDn, &dwPortSSL ); BAIL_ON_FAILURE(hr);
//
// Now do an LDAP Search with Referrals and get the handle to success
// connection. This is where we can find the server the referred object
// resides on
//
hr = _pADs->QueryInterface( IID_IADsObjOptPrivate, (void **)&pADsPrivObjectOptions ); BAIL_ON_FAILURE(hr);
hr = pADsPrivObjectOptions->GetOption ( LDAP_SERVER, (void *)&pszHostName );
BAIL_ON_FAILURE(hr);
//
// additional length 3 is for '\0' and "\\\\"
//
dwLen = STRING_LENGTH(pszHostName) + 3; pszServer = (LPWSTR) AllocADsMem( dwLen * sizeof(WCHAR) ); if (!pszServer) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } wcscpy(pszServer,L"\\\\"); wcscat(pszServer, pszHostName); dwServerSSLSupport = ReadServerSupportsSSL(pszHostName);
if (dwServerSSLSupport == SERVER_STATUS_UNKNOWN || !(dwServerSSLSupport & SERVER_DOES_NOT_SUPPORT_SSL)) {
//
// Try to establish SSL connection for this Password Operation
//
hr = LdapOpenObject( pszHostName, szDn, &pAdsLdpSSL, _Credentials, 636 );
if (SUCCEEDED(hr)) { int retval; SecPkgContext_ConnectionInfo sslattr;
retval = ldap_get_option( pAdsLdpSSL->LdapHandle, LDAP_OPT_SSL_INFO, &sslattr ); if (retval == LDAP_SUCCESS) { //
// If Channel is secure enough, enable LDAP Password Change
//
if (sslattr.dwCipherStrength >= 128) { bUseLDAP = TRUE; } } }
//
// Update the SSL support if appropriate
//
if (dwServerSSLSupport == SERVER_STATUS_UNKNOWN || !bUseLDAP) {
UpdateServerSSLSupportStatus( pszHostName, bUseLDAP ? dwServerSSLSupport : dwServerSSLSupport |= SERVER_DOES_NOT_SUPPORT_SSL ); }
}
if (bUseLDAP) { //
// LDAP Password Set
//
PLDAPModW prgMod[3]; LDAPModW ModDelete; LDAPModW ModAdd; int iOldPwdLen, iNewPwdLen; struct berval* rgBerVal[2]; struct berval* rgBerVal2[2]; struct berval BerVal; struct berval BerVal2;
prgMod[0] = &ModDelete; prgMod[1] = &ModAdd; prgMod[2] = NULL;
ModDelete.mod_op = LDAP_MOD_DELETE | LDAP_MOD_BVALUES; ModDelete.mod_type = L"unicodePwd"; ModDelete.mod_bvalues = rgBerVal; rgBerVal[0] = &BerVal; rgBerVal[1] = NULL; //
// Put old pwd in quotes.
//
if (bstrOldPassword) { iOldPwdLen = (wcslen(bstrOldPassword) + 2) * sizeof(WCHAR); } else { iOldPwdLen = 2 * sizeof(WCHAR); }
pszOldPassword = (LPWSTR) AllocADsMem((iOldPwdLen+1) * sizeof(WCHAR));
if (!pszOldPassword) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); }
wcscpy(pszOldPassword, L"\""); if (bstrOldPassword) { wcscat(pszOldPassword, bstrOldPassword); }
wcscat(pszOldPassword, L"\"");
BerVal.bv_len = iOldPwdLen; BerVal.bv_val = (char*)pszOldPassword;
ModAdd.mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES; ModAdd.mod_type = L"unicodePwd"; ModAdd.mod_bvalues = rgBerVal2; rgBerVal2[0] = &BerVal2; rgBerVal2[1] = NULL; //
// Put new password in ""
//
if (bstrNewPassword) { iNewPwdLen = (wcslen(bstrNewPassword) + 2) * sizeof(WCHAR); } else { iNewPwdLen = 2 * sizeof(WCHAR); }
pszNewPassword = (LPWSTR) AllocADsMem(iNewPwdLen + sizeof(WCHAR));
if (!pszNewPassword) { BAIL_ON_FAILURE(hr = E_FAIL); }
wcscpy(pszNewPassword, L"\""); if (bstrNewPassword) { wcscat(pszNewPassword, bstrNewPassword); } wcscat(pszNewPassword, L"\"");
BerVal2.bv_len = iNewPwdLen; BerVal2.bv_val = (char*)pszNewPassword;
hr = LdapModifyS( pAdsLdpSSL, szDn, prgMod ); BAIL_ON_FAILURE(hr); } else { //
// Password Set using NET APIs
//
NET_API_STATUS nasStatus; DWORD dwParmErr = 0; LPWSTR pszSamAccountArr[] = {L"sAMAccountName"};
//
// Get SamAccountName
//
hr = ADsBuildVarArrayStr( pszSamAccountArr, 1, &varGetInfoEx ); BAIL_ON_FAILURE(hr);
hr = _pADs->GetInfoEx(varGetInfoEx, 0); BAIL_ON_FAILURE(hr); hr = _pADs->Get(L"sAMAccountName", &varSamAccount); BAIL_ON_FAILURE(hr);
#ifndef Win95
//
// At this point if the user credentials are non NULL,
// we want to impersonate the user and then make this call.
// This will make sure the NetUserChangePassword call is made in the
// correct context.
//
if (!_Credentials.IsNullCredentials()) { //
// Need to get the userName and password in the format
// usable by the logonUser call.
//
hr = GetAuthIdentityForCaller( _Credentials, _pADs, &AuthI, FALSE );
if SUCCEEDED(hr) { //
// Note that if this code is backported, then we might
// need to change LOGON32_PROVIDER_WINNT50 to
// LOGON32_PROVIDER_DEFAULT as NT4 and below will support
// only that option. Also note that Win2k and below, do not
// allow all accounts to impersonate.
//
if (LogonUser( AuthI.User, AuthI.Domain, AuthI.Password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT, &hUserToken ) ) { //
// Call succeeded so we should use this context.
//
if (ImpersonateLoggedOnUser(hUserToken)) { fImpersonating = TRUE; } } } // if we could successfully get the auth ident structure.
//
// We will continue to make the ChangePassword call even if
// we could not impersonate successfully.
//
} // if credentials are valid.
#endif
//
// Do the actual change password
//
nasStatus = NetUserChangePassword( pszServer, V_BSTR(&varSamAccount), bstrOldPassword, bstrNewPassword ); #ifndef Win95
if (fImpersonating) { if (RevertToSelf()) { fImpersonating = FALSE; } else { ADsAssert(!"Revert to self failed."); BAIL_ON_FAILURE(hr = HRESULT_FROM_WIN32(GetLastError())); } } #endif
if ( nasStatus == NERR_UserNotFound ) // User not created yet
{ hr = E_ADS_OBJECT_UNBOUND; BAIL_ON_FAILURE(hr); }
hr = HRESULT_FROM_WIN32(nasStatus); BAIL_ON_FAILURE(hr); }
error: if (bstrADsPath) { ADsFreeString(bstrADsPath); }
if (szServerSSL) { FreeADsStr(szServerSSL); }
if (szDn) { FreeADsStr(szDn); }
if (pAdsLdpSSL) { LdapCloseObject(pAdsLdpSSL); }
if (pADsPrivObjectOptions) { pADsPrivObjectOptions->Release(); }
if (pMsgResult) { LdapMsgFree(pMsgResult); }
if (pszOldPassword) { FreeADsMem(pszOldPassword); } if (pszNewPassword) { FreeADsMem(pszNewPassword); }
if (AuthI.User) { FreeADsStr(AuthI.User); }
if (AuthI.Domain) { FreeADsStr(AuthI.Domain); }
if (AuthI.Password) { FreeADsStr(AuthI.Password); } if (pszHostName) { FreeADsStr(pszHostName); }
if (pszServer) { FreeADsMem(pszServer); }
#ifndef Win95
if (fImpersonating) { //
// Try and call revert to self again
//
RevertToSelf(); } #endif
if (hUserToken != INVALID_HANDLE_VALUE ) { CloseHandle(hUserToken); hUserToken = NULL; }
VariantClear(&varSamAccount); VariantClear(&varGetInfoEx);
RRETURN(hr); }
//+---------------------------------------------------------------------------
//
// GetDomainDNSNameFromHost
//
// Given the domain dns name for a host, we need to get hold of the
// dns name for the domain.
//
// Arguments:
// [szHostName] - name of server.
// [Credentials] - Credentials to use for bind.
// [dwPort] - Port to connect to server on.
// [ppszHostName] - ptr to string for retval.
//
// Returns:
// S_OK - If operation succeeds.
// E_* - For other cases.
//
//----------------------------------------------------------------------------
HRESULT GetDomainDNSNameFromHost( LPWSTR szHostName, SEC_WINNT_AUTH_IDENTITY& AuthI, CCredentials& Credentials, DWORD dwPort, LPWSTR * ppszHostName ) { HRESULT hr = S_OK; PADSLDP ld = NULL; LPTSTR *aValuesNamingContext = NULL; IADsNameTranslate *pNameTranslate = NULL; BSTR bstrName = NULL; int nCount = 0;
//
// Bind to the ROOTDSE of the server.
//
hr = LdapOpenObject( szHostName, NULL, // the DN.
&ld, Credentials, dwPort );
BAIL_ON_FAILURE(hr);
//
// Now get the defaultNamingContext
//
hr = LdapReadAttributeFast( ld, NULL, // the DN.
LDAP_OPATT_DEFAULT_NAMING_CONTEXT_W, &aValuesNamingContext, &nCount ); //
// Verify we actuall got back at least one value
//
if (SUCCEEDED(hr) && (nCount < 1)) { hr = HRESULT_FROM_WIN32(ERROR_DS_NO_ATTRIBUTE_OR_VALUE); }
BAIL_ON_FAILURE(hr);
//
// Create nametran object
//
hr = CoCreateInstance( CLSID_NameTranslate, NULL, CLSCTX_ALL, IID_IADsNameTranslate, (void **) &pNameTranslate ); BAIL_ON_FAILURE(hr);
//
// Init with defaultNamingContext and get transalte
//
hr = pNameTranslate->InitEx( ADS_NAME_INITTYPE_SERVER, szHostName, AuthI.User, AuthI.Domain, AuthI.Password ); BAIL_ON_FAILURE(hr);
hr = pNameTranslate->Set( ADS_NAME_TYPE_1779, aValuesNamingContext[0] ); BAIL_ON_FAILURE(hr);
hr = pNameTranslate->Get( ADS_NAME_TYPE_CANONICAL, &bstrName ); BAIL_ON_FAILURE(hr);
if (!bstrName) { BAIL_ON_FAILURE(hr = E_FAIL); }
*ppszHostName = AllocADsStr(bstrName);
if (!*ppszHostName) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); }
//
// Null terminate one place ahead so we can get rid of /
//
(*ppszHostName)[wcslen(bstrName)-1] = L'\0';
error : if (ld) { LdapCloseObject(ld); }
if (pNameTranslate) { pNameTranslate->Release(); }
if (bstrName) { SysFreeString(bstrName); }
if (aValuesNamingContext) { LdapValueFree(aValuesNamingContext); }
RRETURN(hr); }
|