사용자가 암호를 변경할 수 없음 수정(LDAP 공급자)

사용자가 자신의 암호를 변경할 수 있는 기능은 부여하거나 거부할 수 있는 권한입니다. 이 권한을 거부하려면 ADS_ACETYPE_ACCESS_DENIED_OBJECT ace 형식을 사용하여 사용자 개체의 보안 설명자 DACL(임의 액세스 제어 목록)에 개의 API를 설정합니다. 한 ACE는 사용자에 대한 권한을 거부하고 다른 ACE는 모두 그룹에 대한 권한을 거부합니다. 두 ACL은 모두 암호 변경에 대한 확장된 권한의 GUID를 지정하는 개체별 거부 A ACE입니다. 이 권한을 부여하려면 ADS_ACETYPE_ACCESS_ALLOWED_OBJECT ace 형식으로 동일한 API를 설정합니다.

다음 절차에서는 이 권한에 대한 ACL을 수정하거나 추가하는 방법을 설명합니다.

이 권한에 대한 ACL을 수정하거나 추가하려면

  1. 사용자 개체에 바인딩합니다.

  2. 사용자 개체의 ntSecurityDescriptor 속성에서 IADsSecurityDescriptor 개체를 가져옵니다.

  3. IADsSecurityDescriptor.DiscretionaryAcl 속성에서 보안 설명자에 대한 IADsAccessControlList 인터페이스를 가져옵니다.

  4. 개체에 대한 AES를 열거하고 변경 암호 GUID가 있는 ACL({AB721A53-1E2F-11D0-9819-00AA00405)을 검색합니다. IADsAccessControlEntry.ObjectType 속성 및 IADsAccessControlEntry.Trustee 속성에 대한 "Everyone" 또는 "NT AUTHORITY\SELF"의 경우 29B})

    참고

    "Everyone" 및 "NT AUTHORITY\SELF" 문자열은 도메인에서 첫 번째 도메인 컨트롤러의 언어에 따라 지역화됩니다. 이 때문에 문자열을 직접 사용하면 안 됩니다. 계정 이름은 잘 알려진 보안 주체인 "Everyone"("S-1-1-0") 및 "NT AUTHORITY\SELF"("S-1-5-10")에 대한 SID를 사용하여 LookupAccountSid 함수를 호출하여 런타임에 가져와야 합니다. LDAP 공급자(사용자가 암호를 변경할 수 없음)에 표시된 GetSidAccountName,GetSidAccountName_Everyone 및 GetSidAccountName_Self C++ 예제 함수는 이 작업을 수행하는 방법을 보여 줍니다.

     

  5. 사용자가 암호를 변경할 수 없는 경우 ADS_ACETYPE_ACCESS_DENIED_OBJECT 찾은 API의 IADsAccessControlEntry.AceType 속성을 수정하거나 사용자가 암호를 변경할 수 있는 경우 ADS_ACETYPE_ACCESS_ALLOWED_OBJECT.

  6. "Everyone" ACE를 찾을 수 없는 경우 아래 표에 표시된 속성 값이 포함된 새 IADsAccessControlEntry 개체를 만들고 IADsAccessControlList.AddAce 메서드를 사용하여 ACL에 새 항목을 추가합니다.

  7. "NT AUTHORITY\SELF" ACE를 찾을 수 없는 경우 Trustee 속성에 SID "S-1-5-10"("NT AUTHORITY\SELF")의 계정 이름이 포함되어 있다는 점을 제외하고 아래 표에 표시된 것과 동일한 속성 값을 사용하여 새 IADsAccessControlEntry 개체를 만듭니다. IADsAccessControlList.AddAce 메서드를 사용하여 ACL에 항목을 추가합니다.

  8. 개체의 ntSecurityDescriptor 속성을 업데이트하려면 2단계에서 가져온 것과 동일한 IADsSecurityDescriptor를 사용하여 IADs.Put 메서드를 호출합니다.

  9. IADs.SetInfo 메서드를 사용하여 로컬 변경 내용을 서버에 커밋합니다.

  10. AES 중 하나를 만든 경우 ACL의 순서를 다시 지정하여 AES가 올바른 순서로 표시되도록 해야 합니다. 이렇게 하려면 개체의 LDAP ADsPath를 사용하여 GetNamedSecurityInfo 함수를 호출한 다음 동일한 DACL을 사용하여 SetNamedSecurityInfo 함수를 호출합니다. 이 순서 변경은 AES가 추가될 때 자동으로 발생합니다.

다음 표에는 IADsAccessControlEntry 개체 속성 값이 나열되어 있습니다.

IADsAccessControlEntry 속성
AccessMask ADS_RIGHT_DS_CONTROL_ACCESS
AceType ADS_ACETYPE_ACCESS_DENIED_OBJECT 사용자가 암호를 변경할 수 없는지 또는 사용자가 암호를 변경할 수 있는지 ADS_ACETYPE_ACCESS_ALLOWED_OBJECT .
AceFlags 0
플래그 ADS_FLAG_OBJECT_TYPE_PRESENT
ObjectType 문자열 형식의 변경 암호 GUID인 "{AB721A53-1E2F-11D0-9819-00AA0040529B}"입니다.
InheritedObjectType 사용되지 않음
트러스티 SID "S-1-1-0"(모두)의 계정 이름입니다.

 

코드 예

다음 코드 예제에서는 DACL을 변경하는 인터페이스를 가져오는 방법을 보여줍니다. IADsObjectOptions 인터페이스는ADS_SECURITY_INFO_DACL 옵션을 설정하여 사용할 수 있습니다.

참고

이 예제에 설명된 코드를 사용하려면 관리자여야 합니다. 관리자가 아닌 경우 사용자가 클라이언트 쪽 캐시가 Active Directory 도메인 서비스로 다시 플러시되는 방식을 변경할 수 있는 인터페이스를 사용하는 코드를 더 추가해야 합니다.

 

//
// Obtain an IADsObjectOptions interface from the object whose
// DACL you wish to modify.
//
long CanReadSetDACL = ADS_SECURITY_INFO_DACL;

CComPtr<IADsObjectOptions> spObjOps;

hr = pads->QueryInterface(IID_IADsObjectOptions, (void**)&spObjOps);

if(SUCCEEDED(hr))

{

//
// Set the option mask you want to change. In this case
// we want to change the objects security information, so we'll
// use the ADS_OPTION_SECURITY_MASK. Since we want to modify the 
// DACL, we'll set the variant to ADS_SEDCURITY_INFO_DACL flag. 
//

    CComVariant svar;
    svar = CanReadSetDACL;
    hr = spObjOps->SetOption(ADS_OPTION_SECURITY_MASK, svar); 

}
//
// The smart pointer declared for pObjOptions can be released
// or it will be destroyed and released once the pointer goes 
// out of scope.
//

다음 코드 예제에서는 LDAP 공급자를 사용하여 사용자가 암호를 변경할 수 없음 권한을 수정하는 방법을 보여 줍니다. 이 코드 예제에서는 위에 정의된 GetObjectACE 유틸리티 함수를 사용합니다.

이 예제에서는 GetSidAccountName_EveryoneGetSidAccountName_Self C++ 예제 함수를 사용하여 LDAP 공급자(LDAP 공급자)를 읽을 수 없습니다.

#include <aclapi.h>

#define CHANGE_PASSWORD_GUID_W L"{AB721A53-1E2F-11D0-9819-00AA0040529B}"

/***************************************************************************

    CreateACE()

    Creates an ACE and returns the IDispatch pointer for the ACE. This 
    pointer can be passed directly to IADsAccessControlList::AddAce.

    bstrTrustee - Contains the Trustee for the ACE.

    bstrObjectType - Contains the ObjectType for the ACE.

    lAccessMask - Contains the AccessMask for the ACE.

    lACEType - Contains the ACEType for the ACE.

    lACEFlags - Contains the ACEFlags for the ACE.

    lFlags - Contains the Flags for the ACE.

***************************************************************************/

IDispatch* CreateACE(BSTR bstrTrustee, 
                     BSTR bstrObjectType, 
                     long lAccessMask, 
                     long lACEType, 
                     long lACEFlags, 
                     long lFlags)
{
    HRESULT hr;
    IDispatch *pDisp = NULL;
    IADsAccessControlEntry *pACE = NULL;

    hr = CoCreateInstance(CLSID_AccessControlEntry,
                          NULL,
                          CLSCTX_INPROC_SERVER,
                          IID_IADsAccessControlEntry,
                          (void**)&pACE);
    if(SUCCEEDED(hr)) 
    {
        hr = pACE->put_Trustee(bstrTrustee); 
        hr = pACE->put_ObjectType(bstrObjectType);
        hr = pACE->put_AccessMask(lAccessMask); 
        hr = pACE->put_AceType(lACEType);
        hr = pACE->put_AceFlags(lACEFlags);
        hr = pACE->put_Flags(lFlags);

        hr = pACE->QueryInterface(IID_IDispatch, (LPVOID*)&pDisp);

        pACE->Release();
    }

    return pDisp;
}

/***************************************************************************

    ReorderACEs()

    Causes the ACEs of an object DACL to be reordered properly. The ACEs are 
    automatically put in the proper order when they are added to the DACL. 
    On older systems however, this does not occur automatically, so this 
    function is necessary so the deny ACEs are ordered in the list before 
    the allow ACEs.

    pwszDN - A null-terminated Unicode string that contains the LDAP 
    ADsPath of the DS object to reorder the DACL for.

***************************************************************************/

HRESULT ReorderACEs(LPCWSTR pwszDN)
{
    HRESULT hr = E_FAIL;
    DWORD dwResult;
    ACL *pdacl;
    PSECURITY_DESCRIPTOR psd;
    
    dwResult = GetNamedSecurityInfoW(   (LPWSTR)pwszDN,
                                        SE_DS_OBJECT_ALL,
                                        DACL_SECURITY_INFORMATION,
                                        NULL,
                                        NULL,
                                        &pdacl,
                                        NULL,
                                        &psd);

    if(ERROR_SUCCESS == dwResult)
    {
        dwResult = SetNamedSecurityInfoW(   (LPWSTR)pwszDN,
                                            SE_DS_OBJECT_ALL,
                                            DACL_SECURITY_INFORMATION,
                                            NULL,
                                            NULL,
                                            pdacl,
                                            NULL);

        LocalFree(psd);
        
        if(ERROR_SUCCESS == dwResult)
        {
            hr = S_OK;
        }
    }
    
    return hr;
}

/***************************************************************************

    SetUserCannotChangePassword()

    Sets the "User Cannot Change Password" permission using the LDAP provider 
    to the specified setting. To do this, find the existing 
    ACEs and modify the AceType. If the ACE is not found, a new one of the 
    proper type is created and added. The ACEs should always be present, but 
    it is possible that the default DACL excludes them, so this situation 
    will be handled correctly.

    pwszUserDN - A null-terminated Unicode string that contains the LDAP 
    ADsPath of the user object to modify.

    pwszUsername - A null-terminated Unicode string that contains the user 
    name to use for authorization. If this is NULL, the credentials of the 
    current user are used.

    pwszPassword - A null-terminated Unicode string that contains the 
    password to use for authorization. This is ignored if pwszUsername is 
    NULL.

    fCannotChangePassword - Contains the new setting for the privilege. 
    Contains nonzero if the user cannot change their password or zero if 
    the can change their password.

***************************************************************************/

HRESULT SetUserCannotChangePassword(LPCWSTR pwszUserDN, 
                                    LPCWSTR pwszUsername, 
                                    LPCWSTR pwszPassword,
                                    BOOL fCannotChangePassword)
{
    HRESULT hr;

    CComBSTR sbstrEveryone;
    hr = GetSidAccountName_Everyone(&sbstrEveryone);
    if(FAILED(hr))
    {
        return hr;
    }

    CComBSTR sbstrSelf;
    hr = GetSidAccountName_Self(&sbstrSelf);
    if(FAILED(hr))
    {
        return hr;
    }

    if(NULL == pwszUserDN)
    {
        return E_INVALIDARG;
    }
    
    IADs *pads;

    hr = ADsOpenObject( pwszUserDN,
                        pwszUsername,
                        pwszPassword,
                        ADS_SECURE_AUTHENTICATION,
                        IID_IADs, 
                        (LPVOID*)&pads);

    if(SUCCEEDED(hr))
    {
        CComBSTR sbstrSecDesc = "ntSecurityDescriptor";
        CComVariant svar;
        
        hr = pads->Get(sbstrSecDesc, &svar);
        if(SUCCEEDED(hr))
        {
            IADsSecurityDescriptor *psd;

            hr = svar.pdispVal->QueryInterface(IID_IADsSecurityDescriptor, (LPVOID*)&psd);
            if(SUCCEEDED(hr))
            {
                IDispatch *pDisp;

                hr = psd->get_DiscretionaryAcl(&pDisp);
                if(SUCCEEDED(hr))
                {
                    IADsAccessControlList *pACL;

                    hr = pDisp->QueryInterface(IID_IADsAccessControlList, (void**)&pACL);
                    if(SUCCEEDED(hr)) 
                    {
                        BOOL fMustReorder = FALSE;
                        /*
                        Get the existing ACE for the change password permission 
                        for Everyone. If it exists, just modify the existing 
                        ACE. If it does not exist, create a new one and add it 
                        to the ACL.
                        */
                        IADsAccessControlEntry *pACEEveryone = NULL;
                        hr = GetObjectACE(pACL, CHANGE_PASSWORD_GUID_W, sbstrEveryone, &pACEEveryone);
                        if(pACEEveryone)
                        {
                            hr = pACEEveryone->put_AceType(fCannotChangePassword ? 
                                ADS_ACETYPE_ACCESS_DENIED_OBJECT : 
                                ADS_ACETYPE_ACCESS_ALLOWED_OBJECT);

                            pACEEveryone->Release();
                        }
                        else
                        {
                            IDispatch *pDispEveryone = NULL;
                            
                            pDispEveryone = CreateACE(sbstrEveryone, 
                                CComBSTR(CHANGE_PASSWORD_GUID_W),
                                ADS_RIGHT_DS_CONTROL_ACCESS, 
                                fCannotChangePassword ? 
                                    ADS_ACETYPE_ACCESS_DENIED_OBJECT : 
                                    ADS_ACETYPE_ACCESS_ALLOWED_OBJECT, 
                                0,
                                ADS_FLAG_OBJECT_TYPE_PRESENT);
                            
                            if(pDispEveryone)
                            {
                                //add the new ACE for everyone
                                hr = pACL->AddAce(pDispEveryone);

                                pDispEveryone->Release();

                                fMustReorder = TRUE;
                            }                            
                        }
                        
                        /*
                        Get the existing ACE for the change password permission 
                        for Self. If it exists, just modify the existing 
                        ACE. If it does not exist, create a new one and add it 
                        to the ACL.
                        */
                        IADsAccessControlEntry *pACESelf = NULL;
                        hr = GetObjectACE(pACL, CHANGE_PASSWORD_GUID_W, sbstrSelf, &pACESelf);
                        if(pACESelf)
                        {
                            hr = pACESelf->put_AceType(fCannotChangePassword ? 
                                ADS_ACETYPE_ACCESS_DENIED_OBJECT : 
                                ADS_ACETYPE_ACCESS_ALLOWED_OBJECT);
                        
                            pACESelf->Release();
                        }
                        else
                        {
                            IDispatch *pDispSelf = NULL;
                            
                            pDispSelf = CreateACE(sbstrSelf, 
                                CComBSTR(CHANGE_PASSWORD_GUID_W),
                                ADS_RIGHT_DS_CONTROL_ACCESS, 
                                fCannotChangePassword ? 
                                    ADS_ACETYPE_ACCESS_DENIED_OBJECT : 
                                    ADS_ACETYPE_ACCESS_ALLOWED_OBJECT, 
                                0,
                                ADS_FLAG_OBJECT_TYPE_PRESENT);

                            if(pDispSelf)
                            {
                                //add the new ACE for self
                                hr = pACL->AddAce(pDispSelf);

                                pDispSelf->Release();

                                fMustReorder = TRUE;
                            }                            
                        }

                        //update the security descriptor property
                        hr = pads->Put(sbstrSecDesc, svar);
                        
                        //commit the changes
                        hr = pads->SetInfo();

                        if(fMustReorder)
                        {
                            ReorderACEs(pwszUserDN);
                        }

                        pACL->Release();
                    }

                    pDisp->Release();
                }
                
                psd->Release();
            }
        }
    }

    return hr;
}

다음 코드 예제에서는 LDAP 공급자를 사용하여 사용자가 암호를 변경할 수 없음 권한을 수정하는 방법을 보여 줍니다.

참고

아래 예제는 "Everyone" 및 "NT AUTHORITY\SELF" 문자열이 도메인에서 첫 번째 도메인 컨트롤러의 언어에 따라 지역화되기 때문에 기본 언어가 영어인 도메인에서만 작동합니다. Visual Basic에서는 LookupAccountSid 함수를 호출하지 않고도 잘 알려진 보안 주체의 계정 이름을 가져올 수 없습니다. Visual Basic을 사용하는 경우 WinNT 공급자를 사용하여 사용자 암호 변경 안 함 (WinNT 공급자) 수정에 표시된 대로 사용자가 암호 권한을 변경할 수 없음을 수정하는 것이 좋습니다.

 

'******************************************************************************
'
'    SetUserCannotChangePassword
'
'    Sets the "User Cannot Change Password" permission using the LDAP provider
'    to the specified setting. This is accomplished by finding the existing
'    ACEs and modifying the AceType. The ACEs should always be present, but
'    it is possible that the default DACL excludes them. This function will not
'    work correctly if both ACEs are not present.
'
'    strUserDN - A string that contains the LDAP ADsPath of the user object to
'    modify.
'
'    strUsername - A string that contains the user name to use for
'    authorization. If this is an empty string, the credentials of the current
'    user are used.
'
'    strPassword - A string that contains the password to use for authorization.
'    This is ignored if strUsername is empty.
'
'    fCannotChangePassword - Contains the new setting for the privilege.
'    Contains True if the user cannot change their password or False if
'    the can change their password.
'
'******************************************************************************
Sub SetUserCannotChangePassword(strUserDN As String, strUsername As String, strPassword As String, fUserCannotChangePassword As Boolean)
    Dim oUser As IADs
    Dim oSecDesc As IADsSecurityDescriptor
    Dim oACL As IADsAccessControlList
    Dim oACE As IADsAccessControlEntry
    
    fEveryone = False
    fSelf = False
    
    If "" <> strUsername Then
        Dim dso As IADsOpenDSObject
        
        ' Bind to the group with the specified user name and password.
        Set dso = GetObject("LDAP:")
        Set oUser = dso.OpenDSObject(strUserDN, strUsername, strPassword, 1)
    Else
        ' Bind to the group with the current credentials.
        Set oUser = GetObject(strUserDN)
    End If
    
    Set oSecDesc = oUser.Get("ntSecurityDescriptor")
    Set oACL = oSecDesc.DiscretionaryAcl
    
    ' Modify the existing entries.
    For Each oACE In oACL
        If UCase(oACE.ObjectType) = UCase(CHANGE_PASSWORD_GUID) Then
            If oACE.Trustee = "Everyone" Then
                ' Modify the ace type of the entry.
                If fUserCannotChangePassword Then
                    oACE.AceType = ADS_ACETYPE_ACCESS_DENIED_OBJECT
                Else
                    oACE.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
                End If
            End If
        
            If oACE.Trustee = "NT AUTHORITY\SELF" Then
                ' Modify the ace type of the entry.
                If fUserCannotChangePassword Then
                    oACE.AceType = ADS_ACETYPE_ACCESS_DENIED_OBJECT
                Else
                    oACE.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
                End If
            End If
        End If
    Next
    
    ' Update the ntSecurityDescriptor property.
    oUser.Put "ntSecurityDescriptor", oSecDesc
    
    ' Commit the changes to the server.
    oUser.SetInfo
End Sub