除了系统需要维持身分识别外,安全的服务也必须能够维持它的客户端身分识别。您的服务软件应有两个如下所述的目标:
- 识别连接到您服务的客户端。
- 确保服务为连接到您服务的客户端执行 合适 的程序代码,不管客户端产生何种要求。
本章会叙述可以让您用来开发服务的特色,使Windows安全性符合这两个目标。
认识使用者环境
系统在权杖(Token)中维持使用者环境资讯(在第十章中介绍过)。在讨论系统如何使用权杖维持使用者身分识别之前,我们先来讨论系统如何建立权杖的部份。
验证与权杖内容
当您登入一个执行Microsoft Windows 2000的工作站或伺服机器时,通常会输入一个使用者名称及密码。使用者名称是一个信任成员帐户,存放在网域控制站的Active Directory中,或是由一个本地端机器上之安全性资料库所维持的本机使用者帐户。密码则是使用者帐户的 凭证 ,系统用它来 验证 登入者是否合法。
使用者登入系统后,系统会开始收集有关使用者帐户的资讯(除非使用者名称或密码不正确,此种情况表示登入失败)。假如使用者帐户为网域帐户,则系统会收集网域等级的资讯,例如使用者为网域群组的成员。不管登入的帐户是否为网域帐户,系统皆会收集与本地端机器有关的使用者资讯,例如使用者为本机群组的成员(包括经由本机群组成员之网域群组的巢状成员资格)。系统会建立一个被指派给使用者帐户的权限清单,以及所有使用者的群组帐户清单。所有的资讯皆会被编译为系统核心物件内维持的资料结构,也就是权杖。
就Windows安全性而言,在系统验证登入尝试后,权杖是使用者的身分识别。这个身分识别由使用者信任成员帐户的安全识别码(SID)及表格11-1显示之所有资讯构成(有关SIDs及信任成员帐户的资讯,请参阅 第九章 。)
如您所见,权杖中维持着大量的资讯。您可以把权杖视为一个非常详细的「虚拟安全性标志」,权杖使用者SID是印刷在标志上的名称,而群组SIDs则是印刷在标志上的成员资格资讯。权杖中的权限指出某些广泛的系统权利,其馀资料则是主要资讯。在您每次企图存取系统中的安全元件时,它就会参考这个虚拟标志。
权杖及执行程序代码
截至目前为止已经说明了如何建立权杖(一般的规定)及权杖中储存的资讯。现在我必须阐明如何用权杖中的资讯来确保程序代码可被安全地执行。
Windows 2000中的每个程序皆拥有各别的权杖,如您所知,这个权杖即是处理程序的使用者环境。当一个处理程序呼叫CreateProcess时,系统会产生呼叫程序的权杖副本,并把新的权杖与新的处理程序联系在一起。此种方法使您的使用者环境可以传播到整个系统,从您一登入系统到最后的处理程序拥有其中一个权杖为止。在您每次登入系统时,系统便会为您的使用者环境建立一个权杖,然后再启动Windows Explorer,并把权杖与Explorer的程序联系在一起。每次您启动来自Explorer的程序时,新的处理程序就会继承一个原始权杖的副本。直到您的每个处理程序结束,而且Explorer处理程序已离开时,才登出系统、关闭您的使用者环境之最后权杖物件的handle。
| 表格11-1 权杖的内容 |
| 权杖资讯 | 说明 |
|---|---|
| 权杖使用者SID | 一个代表使用者帐户的SID,为它建立权杖。这个帐户通常被称为 权杖使用者 。 |
| 权杖群组SIDs | 群组的SIDs,权杖使用者是群组的成员。这包括内建帐户,例如Everyone及受验证的使用者。 |
| 登入SID | 在验证时建立的唯一SID。这个SID从其他登入工作阶段以唯一的识别登入本工作阶段,即使其他工作阶段为相同的权杖使用者。本章稍后将讨论更多登入SID的细节。 |
| 权限 | 由权杖使用者及群组信任成员帐户持有的权限清单。在第九章曾讨论过权限的细节,本章稍后也有讨论。 |
| 预设拥有者SID | 一个SID,指出在此权杖下执行的程序代码使用预设安全性建立物件的拥有者。在第十章曾讨论预设安全性的内容,本章稍后也有讨论。 |
| 预设群组SID | 一个SID,指出在此权杖下执行的程序代码使用预设安全性建立物件的主要群组。第十章曾讨论预设安全性的内容,本章稍后也有讨论。 |
| 预设DACL | 一个SID,指出在此权杖下执行的程序代码使用预设安全性建立物件的判别存取控制清单(DACL)。第十章曾讨论过预设安全性的内容,本章稍后也有讨论。 |
| 权杖来源 | 一个由建立权杖之系统元件所设定的8个字元「来源名称」。 |
| 权杖类型 | 权杖的类型可以是主要或者模拟的类型,在本章稍后有更详细的讨论。 |
| 限制SIDs | 选择信任成员帐户用来为在此权杖下执行的程序代码限制存取。受限的SIDs在本章稍后有详细的讨论。 |
| 模拟等级 | 权杖的模拟等级指出服务器可充当客户端的等级,本章稍后亦有讨论。 |
| 安全描述项 | 权杖是Windows中的安全物件,而且就如同所有的安全物件,它拥有一个安全描述项以控制对物件的存取。 |
| 凭证资讯 | 某些权杖有凭证资讯,这些权杖可以扮演网路和本地端机器上的权杖使用者。凭证资讯在本章各处有详细的讨论。 |
当您程序中的任何线程执行一个需要了解安全性的系统函数时,系统会检查该程序的权杖资讯。这意味着每次您向系统要求安全物件的handle时(例如登录机码或Mutex(互斥)),系统就会执行存取检查,并查询权杖中必需的资讯。同样地,每次您呼叫例如 GetUserName 的函数或任何其他必需识别执行程序代码之使用者环境函数时,系统会从权杖中撷取资讯。
尽管您可以为处理程序撷取权杖物件的handle,但是为处理程序设定权杖是不可行的(实际上,系统有权为程序设定权杖,但是并不让开发人员经由API使用)。所以在处理程序结束之前,它无法摆脱权杖,或身分识别。Windows的情形也是一样,然而它提供一个非常棒的特色可让您改变在其之下的执行程序代码权杖,称为 模拟 。
模拟的概述
模拟是联系权杖与处理程序中执行的线程所实作的。当您如此做时,此线程(大部分)会用它的 模拟权杖 来呈现身分识别。这个线程被称为「模拟」一个使用者或一个安全性环境。处理程序中的所有其他线程将会继续执行代表处理程序权杖的程序代码,除非它们也模拟了另一个使用者环境。当线程不再需要被视为另一个使用者而工作时,就可以回复到标准状态,这会导致线程再次变回原样并使用处理程序的权杖。如您所想像,模拟在主从架构环境中是非常方便的,在这个环境中,服务器使用不同的安全性环境执行代表客户端的工作。
在继续之前,应该先阐明我的主张,即一个线程(大部分)具有与它的模拟权杖相关之身份识别。通常,在您呼叫要求权杖资讯的函数时,系统会检查线程权杖,如果没有找到,系统会预设为处理程序的权杖。由于线程并非天生就拥有权杖,所以处理程序的权杖即是处理程序原始的安全性环境。然而,在某些个案中,系统函数仍旧会使用处理程序的权杖,甚至是在模拟权杖之下执行呼叫线程的情形。
例如,一个模拟线程呼叫CreateProcess在系统中建立一个新的处理程序,此时新的处理程序会继承与呼叫与处理程序关联的handle,而不是与呼叫线程关联的模拟权杖。此外,任何要求SE_TCB_NAME或SE_ AUDIT_NAME权限的函数(本章稍后至少会讨论一个,即LogonUser)使用程序的权杖而不是检查模拟权杖的线程。本章稍后将会有更多关于模拟的讨论。
您现在可以了解当您登入时,系统如何为您建立权杖。您知道系统如何在您的权杖(或者安全性环境)之下执行程序代码,也知道线程可以使用模拟暂时地呈现另一个身分识别。然而,截至目前为止,我们只讨论过因使用者帐户登入而建立的权杖,例如「JClark」或是「v-JeffrR」。现在您要开始学习在使用者帐户下执行的程序代码如何取得它的权杖。
本机(LocalSystem)帐户
假如您读过第叁章,就应该知道不管使用者是否以互动的方式登入系统皆可以设定让服务执行。您也可以设定让服务像使用者帐户一样地执行,而且要求您输入帐户名称及信任成员帐户的密码。我们可以轻易想像系统秘密地登入这个使用者的情形,以及当执行服务程序代码的程序执行时建立一个权杖来使用。然而,您也可以设定让服务像本机帐户一样的执行,而不需要帐户名称或密码。
本机帐户是Windows 2000系统上的特殊帐户,它不理会服务及其他程序,而就像「操作系统的一部份」一样地执行。结果,本机帐户中的任何程序皆会被指派给权杖,它确实非常有效。以下是一些本机帐户的特性:
- 本机帐户几乎拥有系统中指派的每个权限。
- 本机帐户是内建管理者群组的暗示性成员,而且本身拥有管理者的所有存取权。
- 假如本机帐户没有明确地被授予物件的存取权,则必要时,它可以一直取得物件的拥有权及修改它的DACL。
- 本机帐户拥有SE_TCB_NAME或者「扮演操作系统一部份」权限,所以它可以登入使用者(以获得权杖),并且任意地执行代表使用者的程序代码。
- 本机帐户拥有本地端机器上每件事的直接或间接存取权。
说明
Microsoft Windows NT里的本机帐户经历了一个讽刺的生活-它拥有本地端机器的无限制存取权,而且几乎没有存取网路上其他机器的安全物件。原因是某台机器的本机帐户在另一台机器上没有身分识别(同样的方法,某台机器的管理者帐户在另一台机器上没有意义)。所以其他机器上的物件当然会被保护,以预防本机帐户的存取。
这个限制已经受到注意,然而在网路环境中,网域控制站是使用Active Directory执行Windows 2000的服务器。本机帐户现在于Active Directory中拥有信任成员帐户的状态,因此可以给予网域中其他机器上安全物件的权利。
本机帐户现在没有个别机器被用来做它的监狱。这个改变大大地增加了本机帐户的弹性,但是也出现了潜在的安全性风险,必需被严密地管理。
注意一些提供权杖操作的函数和其他使用者环境—相关的活动通常需要只授予本机帐户的权限。这不是个真实的问题,因为您的服务很有可能在本机帐户中执行。然而,当您正在测试程序代码及试验函数时,例如LogonUser及CreateProcessAsUser(两者在本章稍后都有讨论),这种权限需求会是个麻烦。我们将讨论处理权限议题的方法,以及稍后将讨论其他由本机帐户程序代码引起的问题。
使用者环境及存取控制
尽管前面的章节专注于研究存取控制的部份,而只花了很少的时间集中在使用者环境的重要性。您的处理程序(或者线程)权杖所代表的安全性环境,对存取控制的函数来说绝对是必要的。
每次系统执行存取检查时,它会在物件的DACL中查看符合使用者SID以及权杖中的群组SIDs之存取控制项目(ACEs)。假如您没有明确地允许读取或修改物件的安全性,却要求允许这样做时,系统会比较物件拥有者SID与您权杖中的SIDs,并寻找相配的一组。假如有找到,您会被授予存取权,因为您是物件的拥有者。
如您所见,存取控制完全依赖于权杖。事实上,我们将讨论您如何利用杖严重的影响您的软件及系统中存取控制的方法。愈深入了解这些元件如何一起运作以及在Windows 2000下实作安全性,将会使您确实成为强大的安全性开发人员。
程序设计使用者环境
我们将学习一些强大且有趣的技巧,它可以经由操作Windows 2000的权杖而执行。第一个步骤是取得权杖的handle。让我们经由获得处理程序权杖的handle来开始我们的旅程:
BOOL OpenProcessToken(
HANDLE hProcessHandle,
DWORD dwDesiredAccess,
PHANDLE phTokenHandle);
OpenProcessToken函数会撷取处理程序权杖的handle。hProcessHandle参数是我们想要开启权杖的处理程序handle。不幸地,系统并不让您传递NULL值,所以请指出您要求撷取自己的处理程序权杖之handle。迅速的呼叫GetCurrentProcess传回一个可被传递作为hProcessHandle参数的虚拟handle。dwDesiredAccess参数指出如何使用权杖。您应该只要求程序代码需要的权利。表格11-2叙述可用的权杖存取权利。许多表格中列出的函数,将在本章稍后讨论。
这个变数的位址被您传递作为phTokenHandle,它收到要求的权杖handle。假如OpenProcessToken的传回值是TRUE,则表示此函数执行成功;否则表示失败,这种实例应该呼叫GetLastError以得到更多资讯。
| 表格11-2 权杖存取权利 |
| 值 | 叙述 |
|---|---|
| TOKEN_ADJUST_DEFAULT 当呼叫SetTokenInformtion | (简短地讨论)改变权杖的特色时要求,例如预设的拥有者、主要的群组或是预设的DACL。 |
| TOKEN_ADJUST_GROUPS | 在呼叫AdjustTokenGroups中要求。 |
| TOKEN_ADJUST_PRIVILEGES | 在呼叫AdjustTokenPrivileges中要求。 |
| TOKEN_ADJUST_SESSIONID | 要求调整权杖的工作阶段ID以及SE_TCB_NAME权限。 |
| TOKEN_ASSIGN_PRIMARY | 在呼叫CreateProcessAsUser中使用权杖时要求。 |
| TOKEN_DUPLICATE | 要求复制权杖。 |
| TOKEN_EXECUTE | 等于STANDARD_RIGHTS_EXECUTE。有关标准权利更进一步的讨论,请参阅 第十章 。 |
| TOKEN_IMPERSONATE | 要求与ImpersonateLoggedOnUser一起使用这个权杖。 |
| TOKEN_QUERY | 要求读取任何的权杖资讯,除了使用GetTokenInformation读取它的来源外。 |
| TOKEN_QUERY_SOURCE | 要求使用GetTokenInformation读取权杖的来源。 |
| TOKEN_READ | 结合STANDARD_RIGHTS_READ及TOKEN_QUERY。有关标准权利之更进一步讨论,请参阅 第十章 。 |
| TOKEN_WRITE | 结合STANDARD_RIGHTS_WRITE、TOKEN_ADJUST_ PRIVILEGES、TOKEN_ADJUST_GROUPS及TOKEN_ADJUST_DEFAULT。有关标准权利之更进一步讨论,请参阅 第十章 。 |
| TOKEN_ALL_ACCESS | 完整的存取权杖,结合了所有的权利。 |
OpenProcessToken执行失败的可能原因是存取权利不够;然而,假如您的服务在本机帐户使用者环境中执行,或许它对任何程序的权杖皆会拥有足够的存取(有关存取控制之更多资讯,请参阅 第十章 )。
权杖物件是一个核心物件,而且跟最核心的物件一样,当您结束使用资源时,应该传递权杖物件的handle到CloseHandle中。
说明
当您呼叫OpenProcessToken时收到的handle即是权杖的handle,它会影响该处理程序的使用者环境。调整权杖时所做的任何事,皆会严重且立即影响撷取权杖的处理程序之安全行为。稍后马上会讨论到可对权杖做些什么。有关如何获得处理程序handle的资讯(除了当前的程序外),请参阅《Programming Applications for Microsoft Windows, Fourth Edition (Jeffrey Richter, Microsoft Press, 1999)》一书之第二十二章有关OpenProcess的讨论。