字体: | 推荐给好友 上一篇 | 下一篇

Windows2000 服务器端应用程序开发设计指南-使用者环境

发布: 2008-5-01 20:40 | 作者: Jeffrey Richter Jaso | 来源: 网络转载 | 查看: 265次


说明

尽管登入类型,如LOGON32_LOGON_SERVICE及LOGON32_LOGON_INTERACTIVE,要求了不同的权限,但是实际上它们却不能彻底地产生不同的权杖(权杖之间主要的不同是权杖群组中存在SID,它指出登录使用什么类型,登录的来源也有不同)。要求不同的权限可让系统提供额外的验证等级,而不用使权杖的结构复杂化。

用LOGON32_LOGON_BATCH或LOGON32_LOGON_SERVICE建立的权杖仍旧可以执行与使用者的互动程序,及建立视窗和其他GUI物件。


然而,当系统建立一个如服务一样执行的处理程序时,它使用的权杖是用LOGON32_LOGON_SERVICE登入类型登入的。而当系统以互动方式让使用者登入系统时,它会使用LOGON32_LOGON_INTERACTIVE登入类型。

您传递给dwLogonProvider的值,决定要使用哪个方法验证传递到LogonUser的凭证。除非您正在使用LOGON32_LOGON_NEW_CREDENTIALS登入类型,否则您就应该传递LOGON32_PROVIDER_DEFAULT给所有使用的LogonUser。在这种情形中,您应该传递LOGON32_PROVIDER_WINNT50的值给dwLogonProvider参数。

您会经由LogonUser的phToken参数收到新权杖的handle。

假如系统在验证凭证时执行成功,LogonUser函数将传回TRUE,假如失败则传回FALSE。呼叫GetLastError将传回失败的原因。当LogonUser执行失败时,会产生叁种常见的错误:即ERROR_LOGON_FAILURE、ERROR_LOGON_ TYPE_NOT_GRANTED及ERROR_PRIVILEGE_NOT_HELD。当GetLastError传回ERROR_LOGON_FAILURE时,表示无法识别传递到LogonUser的凭证。假如GetLastError传回ERROR_LOGON_TYPE_NOT_GRANTED,则表示要求的帐户没有要求登入类型的适当帐户权利。错误程序代码ERROR_PRIVILEGE_NOT_HELD暗示呼叫LogonUser的程序没有被授予SE_TCB_NAME权限。

本机帐户预设为拥有此授予的权限,系统中没有预设其他信任成员拥有这个权限。

当您用完这个权杖后,呼叫属于物件的CloseHandle以关闭它。

当您在呼叫LogonUser时,您会要求系统为您建立一个权杖。您可以呼叫CreateProcessAsUser及模拟来使用这个权杖,这是下一节的主题。

模拟(Impersonation)
 

当您在Windows中编写服务器软件时,可以选择建立一个代表客户端的处理程序以执行客户端的请求。然而,为每个客户端建立一个处理程序并不是可调整的技巧。模拟是这个问题的解决办法,因为它让个别的线程在您的客户端安全性环境下执行任一时间长度,然而当它完成,就会回复到程序的安全性环境。模拟可以极有效率的使用并且以可调整的方式完成。

我非常喜爱模拟-系统会为您处理琐碎的细节,而且它经由呼叫ImpersonateLoggedOnUser而被简单地实作。这真的是很棒,因为它保证线程在客户端的安全性环境下执行。有什么更好的方法可保证您不会意外地让客户端导致服务经由本机帐户而滥用授予的可怕权利?以下是ImpersonateLoggedOnUser函数的定义:

BOOL ImpersonateLoggedOnUser( HANDLE hToken);

假如传递给hToken参数的权杖是TokenPrimary类型,则ImpersonateLoggedOnUser会建立一个与TokenImpersonation类型完全一样的权杖,并将它分派到呼叫的线程。假如原始的权杖已经是个模拟权杖,则权杖会被直接分派给线程。在第一个情形下,呼叫的线程必须拥有对权杖的TOKEN_QUERY及TOKEN_DUPLICATE存取权。第二个情况中,则只需要TOKEN_QUERY存取权。

假如ImpersonateLoggedOnUser执行成功,它会传回TRUE。否则您的服务应该呼叫GetLastError函数,以找出失败的原因。

传递给ImpersonateLoggedOnUser的权杖可以是经由呼叫LogonUser、DuplicateTokenEx、OpenThreadToken,或是目前为止我们所讨论过的任何其他类似函数收到的权杖。它也可以经由某些其他方法撷取的权杖(本书将扼要地讨论一些方法)。在执行成功后,多数经由模拟线程呼叫的安全函数能识别由权杖代表的新安全性。


说明

假如现在处于模拟状态的线程下,则呼叫CreateThread建立另一个线程,新的线程将不会被模拟。另一种方式是,除非建立的线程明确地模拟一个权杖,否则所有使用CreateThread建立的线程皆会为了安全的行为而使用程序的权杖。

这会导致某些非常难以发现的错误,因为您的程序代码可以模拟使用者,然后呼叫一个函数建立新的线程,以执行工作。这个新的线程不代表建立线程的安全性环境。在这个开发服务的案例中,通常会授予比预期中更多的存取权给线程,因此会在您的服务中产生一个安全性「漏洞」。


当您的线程代表您的客户端完成执行时,线程会经由呼叫RevertToSelf恢复使用程序的权杖:

BOOL RevertToSelf(VOID);

说明

为了改善模拟时的效能,不管您是否呼叫了LogonUser、OpenProcessToken,或使用某些其他函数取得您用来模拟的权杖,最好要避免取得比需要还多的权杖。通常您的服务会撷取或建立一个handle一次,在连接的状态资料中储存权杖的handle。然后服务在需要时可以使用储存的handle呼叫ImpersonateLoggedOnUser及RevertToSelf 函数,只有在连结被结束且不再需要权杖时才结束handle。


现在您的服务可以经由任何通讯机制来代表客户端执行连接,以使客户端能够传递它的凭证给服务。另外,您的服务可以储存一组事先设定给各种客户端帐户使用的凭证。依据您服务的需求,这个方法是非常有效的。然而,在许多的案例中,一个更无接缝的方法是值得向往的。

模拟连接的客户端
 

Windows提供了一种真正的模拟,它不需要您的服务取得一组凭证。这种模拟是连接导向的,但是除此以外,它与我们所讨论过的模拟技巧很类似。假如一个被授予信任的授权单位已经验证客户端,而且通讯媒介支援模拟时,您的服务可以自动地模拟客户端!

表格11-5列出模拟所支援的连接方式,以及用来初始模拟与回复到程序权杖的函数。

为了讨论每个模拟的种类,我们暂时离开网路通讯的主题,它可以用整本书来讨论。然而,前面章节所提的RoboService范例应用程序使用了命名管道完全地实作模拟的功能,而SSPIChat范例应用程序(在第十二章)则使用Security Support Provider Interface(SSPI)来实作模拟(SSPI也在第十二章讨论)。

表格11-5中谈论到的两个函数可以提出来深入地探讨。它们是ImpersonateSelf及SetThreadToken函数:

BOOL ImpersonateSelf( 

SECURITY_IMPERSONATION_LEVEL ImpersonationLevel);

ImpersonateSelf函数会复制您程序的权杖,建立一个TokenImpersonation类型的权杖,并且分派这个权杖给呼叫的线程。

在实际的情形下,您通常不会使用ImpersonateSelf调整权杖的模拟等级。您或许宁可用它来建立模拟以调整线程,例如启用及停用权限,或启用及停用权杖中的群组,这只会影响个别的线程而非程序中的每个线程。RevertToSelf函数用来结束模拟。

 

评分:0

我来说两句

seccode