还原已删除的对象

Windows Server 2003 包含“还原已删除对象”功能。

要启用还原已删除对象的功能,域中至少有一个域控制器必须在 Windows Server 2003 或更高版本的 Windows 系统上运行。 默认情况下,只有域管理员可以还原已删除的对象,但也可以委托他人进行还原。

以下限制适用于还原已删除的对象:

  • 对象逻辑删除已超出其生存期,则无法还原该对象,因为逻辑删除生存期一过,该对象就会被永久删除。
  • 无法还原存在于命名上下文根中的对象,如域或应用程序分区。
  • 无法还原架构对象。 架构对象绝对不能删除。
  • 还原已删除的容器是有可能的,但还原删除前容器中已删除的对象却很困难,因为必须手动重建容器下的树结构。

还原已删除对象所需的权限

删除对象时,对象安全描述符会被保留。 虽然可以通过安全描述符识别所有者,但出于安全考虑,只有域管理员才允许还原已删除的对象。 域管理员可以通过授予用户或组“恢复逻辑删除”控制访问权限,将还原删除对象的权限授予其他用户和组。 “恢复逻辑删除”控制访问权限是在命名上下文根目录下授予的。 在删除对象后,只有对对象及其属性拥有读取访问权限的用户才能读取该对象及其可访问的属性。

注意

授予用户此权限可能存在安全风险,因为它可能允许用户还原可访问用户通常无法访问的资源的帐户对象。 还原帐户后,用户基本上就获得了对该帐户的控制权,因为用户必须在还原帐户时设置帐户的初始密码。

 

要完全还原已删除的对象,用户必须:

  • 拥有“恢复逻辑删除”或成为拥有该控制访问权限的小组成员。

  • 对每个需要更新的必需属性都拥有写入访问权限。

  • 拥有写入相对可分辨名称 (RDN) 的访问权限。

  • 对每个需要更新的可选属性都拥有写入访问权限。

  • 在目标容器上拥有还原对象的对象类的子创建权限。

    注意

    在还原操作过程中,不会验证 isDeleted 属性。 也不会验证“已删除对象”容器上的删除子对象权限。

     

还原已删除的对象

要还原已删除的对象,首先必须在已删除对象容器中找到该对象。 有关检索已删除对象的详细信息,请参阅检索已删除的对象

找到对象后,必须在单个 LDAP 操作中完成以下操作。 这需要使用 ldap_modify_ext_s 函数和 LDAP_SERVER_SHOW_DELETED_OID 控件。

  • 删除 isDeleted 属性值。 必须删除 isDeleted 属性值,而不是将其设置为 FALSE
  • 替换对象的可分辨名称,以便将其移动到已删除对象容器以外的容器中。 这可以是任何能正常容纳对象的容器。 被删除对象的 lastKnownParent 属性中可以找到该对象上一个容器的可分辨名称。 只有在 Windows Server 2003 域控制器上删除对象时才会设置 lastKnownParent 属性。 因此,lastKnownParent 属性的内容有可能不准确。
  • 还原删除期间被清除的对象的必需属性。

注意

objectCategory 属性也可以在还原对象时设置,但并非必需。 如果未指定 objectCategory 值,则使用对象的 objectClass 的默认 objectCategory 值。

 

还原对象后,可以像删除前一样访问该对象。 此时,应还原任何重要的可选属性。 还必须还原目录中其他对象对该对象的任何引用。

作为一项安全措施,用户对象会在还原时被禁用。 必须在还原可选属性后启用用户对象,才能使用用户对象。

有关如何还原已删除对象的详细信息和代码示例,请参阅下面的 RestoreDeletedObject 函数。

RestoreDeletedObject

以下 C++ 代码示例演示了如何还原已删除的对象。

//***************************************************************************
//
//  RestoreDeletedObject()
//
//  Restores a deleted object. 
//
//  pwszDeletedDN - Contains the fully qualified distinguished name of the 
//  deleted object.
//
//  pwszDestContainerDN - Contains the fully qualified distinguished name of 
//  the folder that the deleted object should be moved to.
//
//  Returns S_OK if successful or an HRESULT or LDAP error code otherwise.
//
//***************************************************************************

HRESULT RestoreDeletedObject(LPCWSTR pwszDeletedDN, LPCWSTR pwszDestContainerDN)
{
    if((NULL == pwszDeletedDN) || (NULL == pwszDestContainerDN))
    {
        return E_POINTER;
    }
    
    HRESULT hr = E_FAIL;

    // Build the new distinguished name.
    LPWSTR pwszNewDN = new WCHAR[lstrlenW(pwszDeletedDN) + lstrlenW(pwszDestContainerDN) + 1];
    if(pwszNewDN)
    {
        wcscpy_s(pwszNewDN, pwszDeletedDN);

        // Search for the first 0x0A character. This is the delimiter in the deleted object name.
        LPWSTR pwszChar;
        for(pwszChar = pwszNewDN; *pwszChar; pwszChar = CharNextW(pwszChar))
        {
            if(('\\' == *pwszChar) && ('0' == *(pwszChar + 1)) && ('A' == *(pwszChar + 2)))
            {
                break;
            }
            
        }

        if(0 != *pwszChar)
        {
            // Truncate the name string at the delimiter.
            *pwszChar = 0;

            // Add the last known parent DN to complete the DN.
            wcscat_s(pwszNewDN, L",");
            wcscat_s(pwszNewDN, pwszDestContainerDN);

            PLDAP ld;

            // Initialize LDAP.
            ld = ldap_init(NULL, LDAP_PORT);
            if(NULL != ld) 
            {
                ULONG ulRC;
                ULONG version = LDAP_VERSION3;

                // Set the LDAP version.
                ulRC = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, (void*)&version);
                if(LDAP_SUCCESS == ulRC)
                {
                    // Establish a connection with the server.
                    ulRC = ldap_connect(ld, NULL);
                    if(LDAP_SUCCESS == ulRC)
                    {                    
                        // Bind to the LDAP server.
                        ulRC = ldap_bind_s(ld, NULL, NULL, LDAP_AUTH_NEGOTIATE);
                        if(LDAP_SUCCESS == ulRC)
                        {
                            // Setup the new values.
                            LPWSTR rgNewVals[] = {pwszNewDN, NULL};

                            /*
                            Remove the isDeleted attribute. This cannot be set 
                            to FALSE or the restore operation will not work.
                            */
                            LDAPModW modIsDeleted = { LDAP_MOD_DELETE, L"isDeleted", NULL };

                            /*
                            Set the new DN, in effect, moving the deleted 
                            object to where it resided before the deletion.
                            */
                            LDAPModW modDN = { LDAP_MOD_REPLACE, L"distinguishedName", rgNewVals };
                            
                            // Initialize the LDAPMod structure.
                            PLDAPModW ldapMods[] = 
                            {
                                &modIsDeleted,
                                &modDN,
                                NULL
                            };

                            /*
                            Use the LDAP_SERVER_SHOW_DELETED_OID control to 
                            modify deleted objects.
                            */
                            LDAPControlW showDeletedControl;
                            showDeletedControl.ldctl_oid = LDAP_SERVER_SHOW_DELETED_OID_W;
                            showDeletedControl.ldctl_value.bv_len = 0;
                            showDeletedControl.ldctl_value.bv_val = NULL;
                            showDeletedControl.ldctl_iscritical = TRUE;

                            // Initialzie the LDAPControl structure
                            PLDAPControlW ldapControls[] = { &showDeletedControl, NULL };

                            /*
                            Modify the specified attributes. This must performed 
                            in one step, which is why the LDAP APIs must be used 
                            to restore a deleted object.
                            */
                            ulRC = ldap_modify_ext_sW(ld, (PWCHAR)pwszDeletedDN, ldapMods, ldapControls, NULL);
                            if(LDAP_SUCCESS == ulRC)
                            {
                                hr = S_OK;
                            }
                            else if(LDAP_ALREADY_EXISTS == ulRC)
                            {
                                /*
                                An object already exists with the specified name 
                                in the specified target container. At this point, 
                                a new name must be selected.
                                */
                            }
                        }
                    }
                }

                if(LDAP_SUCCESS != ulRC)
                {
                    hr = ulRC;
                    
                    OutputDebugString(ldap_err2string(ulRC));
                }

                // Release the LDAP session.
                ldap_unbind(ld);
            }
        }
        else
        {
            /*
            If the end of the string is reached before the delimiter is found, just 
            end and fail.
            */
            hr = E_INVALIDARG;
        }
    
        delete pwszNewDN;
    }
    else
    {
        hr = E_OUTOFMEMORY;
    }

    return hr;
}