Modification de l’utilisateur ne peut pas modifier le mot de passe (fournisseur LDAP)

La possibilité pour un utilisateur de modifier son propre mot de passe est une autorisation qui peut être accordée ou refusée. Pour refuser cette autorisation, définissez deux ACÉ dans la liste de contrôle d’accès discrétionnaire (DACL) du descripteur de sécurité de l’objet utilisateur avec le type ace ADS_ACETYPE_ACCESS_DENIED_OBJECT . Un ACE refuse l’autorisation à l’utilisateur et un autre ACE refuse l’autorisation au groupe Tout le monde. Les deux ACL sont des ACL de refus spécifiques à un objet qui spécifient le GUID de l’autorisation étendue pour la modification des mots de passe. Pour accorder cette autorisation, définissez les mêmes ACÉ avec le type ace ADS_ACETYPE_ACCESS_ALLOWED_OBJECT .

La procédure suivante décrit comment modifier ou ajouter des ACL pour cette autorisation.

Pour modifier ou ajouter les ACL pour cette autorisation

  1. Liez à l’objet utilisateur.

  2. Obtenez l’objet IADsSecurityDescriptor à partir de la propriété ntSecurityDescriptor de l’objet utilisateur.

  3. Obtenez une interface IADsAccessControlList pour le descripteur de sécurité à partir de la propriété IADsSecurityDescriptor.DiscretionaryAcl .

  4. Énumérez les ACÉ de l’objet et recherchez les ACÉ qui ont le GUID de modification du mot de passe ({AB721A53-1E2F-11D0-9819-00AA0040529B}) pour la propriété IADsAccessControlEntry.ObjectType et « Everyone » ou « NT AUTHORITY\SELF » pour la propriété IADsAccessControlEntry.Trustee .

    Notes

    Les chaînes « Tout le monde » et « NT AUTHORITY\SELF » sont localisées en fonction de la langue du premier contrôleur de domaine dans le domaine. Pour cette raison, les chaînes ne doivent pas être utilisées directement. Les noms de compte doivent être obtenus au moment de l’exécution en appelant la fonction LookupAccountSid avec le SID pour « Tout le monde » (« S-1-1-0 ») et « NT AUTHORITY\SELF » (« S-1-5-10 »). Les exemples de fonctions GetSidAccountName, GetSidAccountName_Everyone et GetSidAccountName_Self C++ présentés dans Lecture de l’utilisateur ne peut pas modifier le mot de passe (fournisseur LDAP) montrent comment procéder.

     

  5. Modifiez la propriété IADsAccessControlEntry.AceType des ACÉ qui ont été trouvées pour ADS_ACETYPE_ACCESS_DENIED_OBJECT si l’utilisateur ne peut pas modifier son mot de passe ou ADS_ACETYPE_ACCESS_ALLOWED_OBJECT si l’utilisateur peut modifier son mot de passe.

  6. Si l’ACE « Tout le monde » est introuvable, créez un objet IADsAccessControlEntry qui contient les valeurs de propriété affichées dans le tableau ci-dessous et ajoutez la nouvelle entrée à la liste de contrôle d’accès avec la méthode IADsAccessControlList.AddAce .

  7. Si l’ace « NT AUTHORITY\SELF » est introuvable, créez un objet IADsAccessControlEntry avec les mêmes valeurs de propriété indiquées dans le tableau ci-dessous, sauf que la propriété Trustee contient le nom du compte pour le SID « S-1-5-10 » (« NT AUTHORITY\SELF »). Ajoutez l’entrée à la liste de contrôle d’accès avec la méthode IADsAccessControlList.AddAce .

  8. Pour mettre à jour la propriété ntSecurityDescriptor de l’objet, appelez la méthode IADs.Put avec le même IADsSecurityDescriptor obtenu à l’étape 2.

  9. Validez les modifications locales sur le serveur avec la méthode IADs.SetInfo .

  10. Si l’une des ASEE a été créée, vous devez réorganiser la liste de contrôle d’accès afin qu’elles soient dans l’ordre correct. Pour ce faire, appelez la fonction GetNamedSecurityInfo avec l’ADsPath LDAP de l’objet, puis la fonction SetNamedSecurityInfo avec le même DACL. Cette réorganisation se produit automatiquement lorsque les ACL sont ajoutés.

Le tableau suivant répertorie les valeurs de propriété de l’objet IADsAccessControlEntry .

IADsAccessControlEntry, propriété Valeur
Masque d’accès ADS_RIGHT_DS_CONTROL_ACCESS
AceType ADS_ACETYPE_ACCESS_DENIED_OBJECT si l’utilisateur ne peut pas modifier son mot de passe ou ADS_ACETYPE_ACCESS_ALLOWED_OBJECT si l’utilisateur peut modifier son mot de passe.
AceFlags 0
Indicateurs ADS_FLAG_OBJECT_TYPE_PRESENT
Objecttype « {AB721A53-1E2F-11D0-9819-00AA0040529B} » qui est le GUID de modification du mot de passe sous forme de chaîne.
InheritedObjectType Non utilisé
Tiers de confiance Nom du compte pour le SID « S-1-1-0 » (Tout le monde).

 

Exemple de code

L’exemple de code suivant montre comment obtenir une interface pour modifier une liste DACL. L’interface IADsObjectOptions peut être utilisée en définissant l’option ADS_SECURITY_INFO_DACL.

Notes

Pour utiliser le code documenté dans cet exemple, vous devez être administrateur. Si vous n’êtes pas administrateur, vous devez ajouter du code supplémentaire qui utilisera une interface qui permettra à l’utilisateur de modifier la façon dont le cache côté client est vidé dans le service domaine 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.
//

L’exemple de code suivant montre comment modifier l’autorisation Utilisateur ne peut pas modifier le mot de passe à l’aide du fournisseur LDAP. Cet exemple de code utilise la fonction utilitaire GetObjectACE définie ci-dessus.

Cet exemple utilise les GetSidAccountName_Everyone et GetSidAccountName_Self exemples de fonctions C++ indiqués dans Lecture de l’utilisateur ne peut pas modifier le mot de passe (fournisseur 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;
}

L’exemple de code suivant montre comment modifier l’autorisation Utilisateur ne peut pas modifier le mot de passe à l’aide du fournisseur LDAP.

Notes

L’exemple ci-dessous fonctionne uniquement sur les domaines où la langue principale est l’anglais, car les chaînes « Everyone » et « NT AUTHORITY\SELF » sont localisées en fonction de la langue du premier contrôleur de domaine dans le domaine. Dans Visual Basic, il n’existe aucun moyen d’obtenir les noms de compte d’un principal de sécurité connu sans appeler la fonction LookupAccountSid . Si vous utilisez Visual Basic, il est recommandé d’utiliser le fournisseur WinNT pour modifier l’autorisation Utilisateur ne peut pas modifier le mot de passe, comme indiqué dans Modification de l’utilisateur ne peut pas modifier le mot de passe (fournisseur 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