//--------------------------------------------------------------------------- // // 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 #include #include "..\ldapc\ldpcache.hxx" #include "..\ldapc\ldaputil.hxx" #include "..\ldapc\parse.hxx" #include #include 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(&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); }