| 表格11-5 模拟支援的连接方式 |
| 连接方式 | 模拟函数 |
|---|---|
| 非连接 |
BOOL ImpersonateLoggedOnUser( |
| 命名管道 |
BOOL ImpersonateNamedPipeClient( |
| 动态资料交换 |
BOOL DdeImpersonateClient( |
| 远端程序呼叫 (RPC) |
RPC_STATUS RPC_ENTRY RpcImpersonateClient( |
| 通讯端或任何其他传输机制(经由SSPI)-在第十二章中讨论 |
SECURITY_STATUS ImpersonateSecurityContext( |
以下列出的SetThreadToken函数,可让您使用线程的handle任意地选择任何线程的模拟权杖。pThread参数是个指向您要调整的线程handle指标,传递NULL以指出当前的线程。hToken参数指出用来模拟的权杖,NULL值将导致线程回复成程序等级的权杖。
BOOL SetThreadToken(
PHANDLE pThread,
HANDLE hToken);
起先粗略地浏览时,发觉SetThreadToken函数似乎是不必要的。为什么不就使用适合您通讯机制的模拟函数呢?答案是,模拟缺少了一样特色-即模拟堆叠 的概念。让我来解释一下。
假如您用权杖A呼叫ImpersonateLoggedOnUser,然后再用权杖B呼叫一个呼叫到ImpersonateLoggedOnUser的函数,当这个函数在呼叫RevertToSelf时,系统将会使线程的权杖回复到程序权杖。系统没有记忆线程跟权杖A的结合。通常,您会控制您的程序代码并避免像这样的情况。但是以下有两个值得注意的例外情形:
- 在较大的专案里,进入您函数前的状态是不稳定或不一致的。
- 被用来扩充其他软件的DLL专案,例如扩充Microsoft Internet Information Services的ISAPI。
为了伪造模拟堆叠,您的函数必需经由呼叫OpenThreadToken,以撷取其当前权杖的handle(我们已经讨论过),把它储存起来(最有可能在堆叠上),然后再呼叫适合的模拟函数,而非呼叫相符的「回复」函数,您的程序代码需要使用SetThreadToken以恢复被储存的权杖。这个方法使您的函数用它找到线程的方法把线程带回呼叫的程序代码中。
说明
在高效能的服务器环境中,网路上的通讯模拟资讯开销是不受欢迎的。在许多情况下,您可以经由在连结上呼叫适合的模拟函数来达到最好的效能,然后再呼叫OpenThreadToken撷取及储存模拟线程权杖的handle。之后,当服务中的线程执行客户端更进一步的请求时,您可以使用SetThreadToken或ImpersonateLoggedOnUser模拟储存的权杖。
受限的权杖
如同前面所提的,若要您的服务或任何应用程序从头开始建立一个权杖是不可能的-系统必须为您建立权杖。但是Windows可让您以现存的权杖为基础,再加上一些额外的限制以建立一个新的权杖。这个新的权杖即是受限的权杖。受限的权杖可帮助您的软件用非常清楚的解决办法来符合一些复杂的安全性需求。
当您建立一个受限的权杖时,并不是把限制加入现存的权杖中;反而是使用一个范本权杖建立一个新的权杖,虽然这个新的权杖会有额外的限制。以下列出叁种可让您放置在权杖上的限制。每个限制是非必需的,您可以在建立新权杖时选择它们的任何一种组合。有两种限制需依赖对存取控制的认识,它们已经在第十章中讨论。您可以在权杖上执行以下所列之行为的任何一种组合,用来建立一个新的受限权杖:
- 删除权限。
- 停用信任成员帐户的权杖SIDs。
- 增加信任成员帐户的「受限SIDs」。
删除权限
在建立一个受限的权杖时,可以选择一组您不想授予新权杖的权限,它与停用的权限不同。并不是您明确地指定从新权杖中移除权限。您可能会有想使用现存的安全性环境,但是需要删除某些权限的情况。例如,您可能会使用自己的环境,或者经由模拟而收到并移除SE_SHUTDOWN_NAME或SE_TCB_NAME权限的环境。
停用权杖SIDs
在建立一个受限的权杖时,可以选择停用现存权杖中的哪个信任成员。被停用的信任成员可以是权杖使用者及群组成员的任一种组合。当存取检查被执行时,任何被选择加入停用清单的SIDs的信任成员将只被用来拒绝存取。
例如,假设权杖使用者具有TEMP USERS群组中的成员资格,当拒绝使用者存取C:\Permanent目录,而授予使用者存取C:\Temp目录时,假如TEMP USERS群组的SID被选为受限权杖中的停用SID,则群组中的成员资格将不再被允许存取C:\Temp目录,但是它仍旧被拒绝存取C:\Permanent目录。
您可以只停用已经存在于原始权杖的SIDs,而停用权杖中的信任成员不会以任何方法影响权杖的身分识别。例如,停用权杖使用者只会影响物件的存取权。权杖仍旧会识别特定的使用者。
增加受限的SIDs
除了停用现存的SIDs外,您还可以建立一组信任成员或SIDs,即受限的SIDs,并将它动态地加入您的新权杖。您可能会认为动态地加入信任成员到权杖的能力是非常强大的。但是其隐藏的困难是新的SIDs会被用来作为存取安全物件的再确认。不只存取检查必须在存取被授予前即清除权杖「天生的」信任成员,它也必须清除一组新的受限信任成员。
使用受限的SIDs与建立次要的权杖类似,它会确保权杖在物件上执行任可行为前,已经拥有安全物件的存取权。但是使用受限的权杖时,这个次要检查会被系统自动地处理。受限的SIDs稍微改变存取检查,实际上它们必须被执行两次:一次是权杖中天生的SIDs,另一次则是受限的SIDs。只有当两者都执行成功时,才表示允许存取检查(有关存取检查的更详细讨论,请参阅 第十章 )。
您现在已经了解了可以应用于新受限权杖副本的限制。让我们看看这个建立 权杖 的函数,CreateRestrictedToken:
BOOL CreateRestrictedToken(
HANDLE hExistingTokenHandle,
DWORD dwFlags,
DWORD dwDisableSidCount,
PSID_AND_ATTRIBUTES pSidsToDisable,
DWORD dwDeletePrivilegeCount,
PLUID_AND_ATTRIBUTES pPrivilegesToDelete,
DWORD dwRestrictedSidCount,
PSID_AND_ATTRIBUTES pSidsToRestrict,
PHANDLE phNewTokenHandle);
hExistingTokenHandle参数是新权杖被建立的原始权杖。CreateRestrictedToken以传递给hExistingTokenHandle参数的权杖handle为基础,建立一个新的权杖。
这个handle必须有TOKEN_DUPLICATE的存取权。任何作为授限权杖的原始权杖皆是合法的,除了已经有授限SIDs清单的权杖外。您可以经由传递它的handle到IsTokenRestricted函数,以查明权杖是否包含了受限SIDs:
BOOL IsTokenRestricted(
HANDLE TokenHandle);
假如您想让新的权杖只允许不需要权限的功能时,可以传递DISABLE_MAX_PRIVILEGE给dwFlags参数。假如您只想删除权限的子集合,可以传递0。传递0是较常见的情况。
dwDisableSidCount参数指出您要确保在新的权杖中被停用的信任成员数量。下一个参数为pSidsToDisable,指向一个SID_AND_ATTRIBUTES结构的阵列,它指出新权杖中停用的群组(或许是权杖使用者)。dwDisableSidCount参数涉及传递给这个参数的项目数量。停用SIDs的特点是新的权杖中存在之SID的SE_GROUP_USE_FOR_DENY_ONLY属性资讯。
说明
您可以传递原始权杖中表示的信任成员超集(superset)给pSidsToDisable参数。系统将会忽略不在原始权杖中的任何SID。这样一来,您就可以将停用SIDs的个别清单运用到多于一个拥有不同基本权杖使用者及群组的权杖中。
SID_AND_ATTRIBUTES结构之定义如下:
typedef struct _SID_AND_ATTRIBUTES {
PSID Sid;
DWORD Attributes;
}SID_AND_ATTRIBUTES ;此结构的Attributes成员被CreateRestrictedToken所忽视。其他函数,例如GetTokenInformation,使用Attributes成员记录SID的属性,例如SE_GROUP_USE_FOR_DENY_ONLY及SE_GROUP_MANDATORY即是。SID_AND_ATTRIBUTES的Sid成员指向一个SID结构,它指出您想在新的权杖中停用的信任成员。
您的服务器应该传递一个SID_AND_ATTRIBUTES结构的阵列给CreateRestrictedToken的pSidsToDisable参数。假如您不想要停用新权杖中的任何信任成员时,应该传递0给dwDisableSidCount及NULL给pSidsToDisable参数。
除了pPrivilegesToDelete指向一个LUID_AND_ATTRIBUTES结构的阵列之外,CreateRestrictedToken的dwDeletePrivilegeCount及pPrivilegesToDelete参数与「停用的SID」参数的运作同样。当在使用停用的SIDs时,如果您不想删除任何权限,则传递0及NULL给dwDeletePrivilegeCount及pPrivilegesToDelete是适当的。有关LUID_AND_ATTRIBUTES结构的详细讨论,请参阅本章前面之〈 调整权杖的权限 〉一节。
dwRestrictedSidCount及pSidsToRestrict参数分别表示您要加到新权杖中,作为「受限SIDs」的信任成员的总数及阵列。您应该使用与dwDisableSidCount及pSidsToDisable相同的规则来建立您的受限信任成员清单。请记得您不需要在新的权杖中包括任何的受限SID。假如不需要受限SIDs的话,可以传递0给dwRestrictedSidCount参数。
您传递给CreateRestrictedToken的停用SIDs阵列与受限SIDs阵列之间的不同处在于受限SIDs,其函数不会忽略不存在于原始权杖的信任成员。事实上,您时常会含括未包含于原始权杖中的信任成员,以更进一步地限制存取检查。
CreateRestrictedToken的最后一个参数,即phNewTokenHandle,指向一个HANDLE变数,它会接收一个新的、受限权杖的handle。
CreateRestrictedToken是一个强大的函数,可让您有弹性的限制现存的权杖。例如,思考以下这个说明弹性的方案。想像您要保护物件群组的安全,以不是很正统的方式明确地允许或拒绝使用者的某些存取权。您只想要在星期二限制某些物件的存取权,而不管这个物件存在的特有存取权。您可以采取以下步骤来实作这个功能,而不用破坏此物件之星期二以外的特有存取权。以下是管理的工作:
- 为了增加限制到安全物件的唯一目的,建立一个名为星期二的使用者帐户。
- 修改物件的DACLs,包括星期二要求的限制,并分派这些拒绝存取ACEs到星期二帐户的SID。
以下为服务器的工作:
- 当使用者连接到您的服务器时,使用GetSystemTime函数决定是否为星期二。
- 假如是星期二,则在执行代表客户端的程序代码之前建立一个受限权杖,而不是采用使用者的模拟权杖。
- 建立符合来源权杖内之群组的受限SIDs清单,并包含权杖的使用者SID。同时包含一个星期二信任成员项目在受限SIDs清单中。
- 使用ImpersonateLoggedOnUser模拟新的受限权杖。
使用这个程序,您可以轻易地执行附加限制的功能,而不用每个星期二就请求改变所有安全物件的存取控制一次。
TokenMaster范例应用程序可让您撷取权杖,然后建立一个权杖的受限版本。您可以使用这个受限版本启动应用程序。这对于您更进一步了解受限权杖,会是个非常有用的工具。
在阅读本章之后,您已经熟悉可让软件维护及与Windows存取控制模组一致的弹性方式,以管理身分识别的工具及技巧。您也已经了解用权杖为系统执行使用者环境的方式对您的软件很有帮助。同时也了解模拟也对您的软件极有好处,它可让您代表您的客户端执行工作(第十二章把这个主题带到下一个层级,SSPI)。受限权杖是另一个强大的特色,您可以在要求大量存取控制弹性的安全专案中使用它。
在您愈了解本章讨论的特色和能力以及最后两章所涵盖的特色后,您就愈能够设计为您安全工作的软件。