代码之家  ›  专栏  ›  技术社区  ›  Luke

临时模拟并启用权限?

  •  2
  • Luke  · 技术社区  · 16 年前

    我们维护一个dll,它可以做很多与系统相关的事情;遍历文件系统、注册表等。这个dll的调用方可能正在使用模拟,也可能没有使用模拟。为了更好地支持所有可能的场景,我试图将其修改为更聪明。我将使用删除文件的示例。目前我们只调用deletefile(),如果失败,就到此为止。我想到了以下几点:

    BOOL TryReallyHardToDeleteFile(LPCTSTR lpFileName)
    {
        // 1. caller without privilege
        BOOL bSuccess = DeleteFile(lpFileName);
        DWORD dwError = GetLastError();
        if(!bSuccess && dwError == ERROR_ACCESS_DENIED)
        {
            // failed with access denied; try with privilege
            DWORD dwOldRestorePrivilege = 0;
            BOOL bHasRestorePrivilege = SetPrivilege(SE_RESTORE_NAME, SE_PRIVILEGE_ENABLED, &dwOldRestorePrivilege);
            if(bHasRestorePrivilege)
            {
                // 2. caller with privilege
                bSuccess = DeleteFile(lpFileName);
                dwError = GetLastError();
                SetPrivilege(SE_RESTORE_NAME, dwOldRestorePrivilege, NULL);
            }
            if(!bSuccess && dwError == ERROR_ACCESS_DENIED)
            {
                // failed with access denied; if caller is impersonating then try as process
                HANDLE hToken = NULL;
                if(OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, TRUE, &hToken))
                {
                    if(RevertToSelf())
                    {
                        // 3. process without privilege
                        bSuccess = DeleteFile(lpFileName);
                        dwError = GetLastError();
                        if(!bSuccess && dwError == ERROR_ACCESS_DENIED)
                        {
                            // failed with access denied; try with privilege
                            bHasRestorePrivilege = SetPrivilege(SE_RESTORE_NAME, SE_PRIVILEGE_ENABLED, &dwOldRestorePrivilege);
                            if(bHasRestorePrivilege)
                            {
                                // 4. process with privilege
                                bSuccess = DeleteFile(lpFileName);
                                dwError = GetLastError();
                                SetPrivilege(SE_RESTORE_NAME, dwOldRestorePrivilege, NULL);
                            }
                        }
                        SetThreadToken(NULL, hToken);
                    }
                    CloseHandle(hToken);
                    hToken = NULL;
                }
            }
        }
        if(!bSuccess)
        {
            SetLastError(dwError);
        }
        return bSuccess;
    }
    

    所以首先它会以来电者的身份尝试。如果访问被拒绝而失败,它将临时启用调用方令牌中的权限并重试。如果访问被拒绝而调用方正在模拟时失败,则它会暂时无人参与并重试。如果访问被拒绝而失败,它将临时启用进程令牌中的权限并重试。我认为这应该能处理任何情况,但我想知道是否有更好的方法来实现这一点?有很多操作我们可能希望使用此方法(即几乎所有访问安全对象的操作)。

    3 回复  |  直到 16 年前
        1
  •  0
  •   Michael Howard-MSFT    16 年前

    唯一的方法是作为服务运行,并从服务中模拟用户。服务 必须 被授予模拟特权,默认情况下所有服务帐户都被授予该特权。当您模拟调用者时,您可能需要模拟委派,以便您可以轻松离开计算机。

        2
  •  1
  •   Chris Smith    16 年前

    备份和还原特权一起将提供对所有文件的完全访问,完全停止。这些可用于本地系统。要使用此命令,必须打开具有文件标志备份语义的文件。有些win32api不是设计用来与此一起使用的,并且不会将标志传递给内核,尽管在某些情况下,您可以使用createfile来打开目录。(对内核来说,目录只是另一种文件)。

    如果您真的需要能够访问所有内容,我会说启用这些特权并执行应该成功的扫描操作,而不管调用方的安全性如何。

    一个悬而未决的问题是,这些文件可以被锁定或打开,但不能共享访问权限。无法从用户模式绕过这一点(如果不终止拥有资源的进程,可能是过度终止)。这就是我知道的主流扫描器使用内核模式文件系统过滤器驱动程序实现此功能的原因。

    另外,考虑审计:您希望本地系统或与调用进程相关联的用户显示审计条目吗?

        3
  •  0
  •   Michael Howard-MSFT    16 年前

    对于一个dll来说,这一切看起来真的很可怕!这听起来像是一份 服务 而不是dll,或者,如果您要允许用户帐户删除特权帐户放在那里的数据,那么为什么不简单地在允许删除操作的对象上设置acl呢?

    说到这里,你到底想干什么?用户帐户通常不能删除由管理员帐户放在那里的数据!