假如您认为一定也有一个获得线程权杖的方法(假定线程在那时是模拟的),那么您是正确的。您可以使用OpenThreadToken:
BOOL OpenThreadToken(
HANDLE hThreadHandle,
DWORD dwDesiredAccess,
BOOL fOpenAsSelf,
PHANDLE phTokenHandle);
注意,OpenThreadToken与OpenProcessToken非常相似,除了第一个参数是线程而非处理程序的handle外,它拥有附加的参数fOpenAsSelf。这个参数向系统指出您想要 为谁 开启权杖,让我来做个说明。
请记住当您呼叫OpenThreadToken时,会要求一个Windows中的安全物件-即权杖。这意味着您可能或不可能拥有物件的存取权。您也应该记住除了处理程序的安全性环境外,也能够在可以被模拟成安全性环境的线程中执行。因为当它为模拟时,撷取自己的权杖handle之线程是非常常见的,系统可根据您的处理程序权杖指出开启一个线程权杖在执行时所需的所有存取检查。假如您想要对程序权杖执行存取检查时,应该传递TRUE给OpenThreadToken的fOpenAsSelf参数。假如您想对您的线程模拟权杖执行存取检查时,应该传递FALSE。
假如您的线程现在没有模拟,这些参数就没有意义(本章稍后将讨论更多模拟的细节)。
就像OpenProcessToken一样, OpenThreadToken的phTokenHandle参数会传回要求的权杖handle。假如OpenThreadToken的传回值为TRUE,表示此函数成功;否则函数失败,您可以呼叫GetLastError得到更多资讯。
假如线程没有模拟时,OpenThreadToken函数将失败而且GetLastError将传回ERROR_NO_TOKEN。
您现在拥有了权杖的handle。您可能会问,可以对它作什么事呢?请继续读下去。
读取权杖资讯
表格11-1列出的大部分权杖资讯也可以从权杖中读取,假定呼叫的程序代码拥有对物件的TOKEN_QUERY(或是TOKEN_QUERY_SOURCE)存取权。您应该使用GetTokenInformation函数找出权杖的内容:
BOOL GetTokenInformation(
HANDLE hTokenHandle,
TOKEN_INFORMATION_CLASS tokenInformationClass,
PVOID pTokenInformation,
DWORD dwTokenInformationLength,
PDWORD pdwReturnLength);
您应该传递权杖的handle为hTokenHandle参数。 tokenInformationClass参数指出您想从权杖获得的资讯,而pTokenInformation参数是指向缓冲器的指标,缓冲器填满要求的资讯。dwTokenInformationLength参数指出您传递的缓冲器长度,而pdwReturnLength参数指向一个收到撷取资讯必需的缓冲器大小的变数。
依据tokenInformationClass参数,经由pTokenInformation参数指向多种的缓冲器型别。以下的清单叙述您可从权杖中撷取的资讯:资讯类别值及使用的资料类型(TOKEN_INFORMATION_CLASS也与SetTokenInformation函数一起使用,在本章稍后讨论)。
- TokenUser 传回权杖使用者的SID。登入系统时,帐户名称被用来建立权杖的使用者(这个值不与SetTokenInformation一起使用)。
typedef struct _TOKEN_USER {
SID_AND_ATTRIBUTES User;
}TOKEN_USER; - TokenDefaultDacl 用来读取或设定权杖的预设DACL。有关预设DACLs之更进一步讨论以及建立DACL的详细讨论,请参阅 第十章 的内容。在DACL中设定资讯时需要拥有TOKEN_ADJUST_ DEFAULT存取权利。
typedef struct _TOKEN_DEFAULT_DACL {
PACL DefaultDacl;
}TOKEN_DEFAULT_DACL; - TokenOwner 用来读取或设定权杖物件的预设拥有者(有关物件所有权的讨论,请参阅 第十章 的内容)。TOKEN_ADJUST_DEFAULT存取权利对设定权杖的拥有者来说是必要的。
typedef struct _TOKEN_OWNER {
PSID Owner;
}TOKEN_OWNER; - TokenPrimaryGroup 读取或设定权杖的主要群组。在呼叫SetTokenInformation时,会要求TOKEN_ADJUST_DEFAULT存取权利。
typedef struct _TOKEN_PRIMARY_GROUP {
PSID PrimaryGroup;
}TOKEN_PRIMARY_GROUP; - TokenGroups 传回与权杖关联的群组SIDs(这个值不与SetTokenInformation一起使用)。
typedef struct _TOKEN_GROUPS {
DWORD GroupCount;
SID_AND_ATTRIBUTES Groups [ANYSIZE_ARRAY ];
}TOKEN_GROUPS; - TokenPrivileges 传回与权杖关联的权限(这个值不与SetTokenInformation一起使用)。
typedef struct _TOKEN_PRIVILEGES {
DWORD PrivilegeCount;
LUID_AND_ATTRIBUTES Privileges [ANYSIZE_ARRAY ];
}TOKEN_PRIVILEGES; - TokenSource 传回权杖的来源。这是一个表示建立权杖项目的文字字串。TOKEN_QUERY_SOURCE存取权利对撷取这个资讯来说是必要的(这个值不与SetTokenInformation一起使用)。
typedef struct _TOKEN_SOURCE {
CHAR SourceName [8 ];
LUID SourceIdentifier;
}TOKEN_SOURCE; - TokenType 传回权杖的类型。可能的值为TokenPrimary及Token Impersonation。GetTokenInformation的pTokenInformation参数将传回一个个别的TOKEN_TYPE指出权杖的类型(这个值不与SetTokenInformation一起使用)。
typedef enum _TOKEN_TYPE {
TokenPrimary =1,
TokenImpersonation
}TOKEN_TYPE; - TokenImpersonationLevel 传回模拟等级。更多相关资讯请参阅 表格11-3 (这个值不与SetTokenInformation一起使用)。
typedef enum _SECURITY_IMPERSONATION_LEVEL {
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
}SECURITY_IMPERSONATION_LEVEL; - TokenStatistics 传回有关权杖的一般资讯。它的成员包括GroupCount、PrivilegeCount及ModifiedId。ModifiedId成员是本机唯一识别元(LUID),每当权杖被修改时就会改变。您的程序代码可以使用这个值来检查自从上次检查后,权杖是否已被改变。这种侦查能力在编写呼叫到协力厂商DLLs或程序库的容错程序代码时非常有帮助(这个值不与SetTokenInformation一起使用)。
typedef struct _TOKEN_STATISTICS {
LUID TokenId;
LUID AuthenticationId;
LARGE_INTEGER ExpirationTime;
TOKEN_TYPE TokenType;
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
DWORD DynamicCharged;
DWORD DynamicAvailable;
DWORD GroupCount;
DWORD PrivilegeCount;
LUID ModifiedId;
}TOKEN_STATISTICS; - TokenRestrictedSids 传回权杖的受限SIDs。本章稍后将讨论受限权杖的细节(这个值不与SetTokenInformation一起使用)。
typedef struct _TOKEN_GROUPS {
DWORD GroupCount;
SID_AND_ATTRIBUTES Groups [ANYSIZE_ARRAY ];
}TOKEN_GROUPS; - TokenSessionId 指出终端服务器权杖的工作阶段ID。GetTokenInformation的pTokenInformation参数将传回一个DWORD值。假如终端服务器没有安装在本地端机器或权杖与终端服务器主控台关联的话,DWORD的值会是0。若权杖与终端服务器的客户端关联,则DWORD的值是客户端的工作阶段ID(这个值不与SetTokenInformation一起使用)。
如同前面清单显示,您可以从权杖中读取大量的资讯。以下是从权杖撷取的最常见项目:
- 权杖使用者SID 权杖所代表的使用者帐户。通常会从权杖收到这个资讯,并找出谁正在执行程序代码。您可以传递这个SID到LookupAccountSid中(在第九章讨论),以取得使用者帐户的文字名称。
- 登入SID 这个SID与权杖群组一起掩藏,所以会花费一些时间挖掘,以取回它(我们将会马上讨论)。然而,登录SID对于唯一地识别工作阶段是非常方便的。假如某位使用者不只一次登入执行Windows 2000的机器(以互动式或者透过其他方法)时,系统会为每个工作阶段建立一个唯一的登入SID,不管权杖使用者从一个工作阶段到另一个工作阶段是否相同。
- Token groups 对找出与权杖关联的群组很有帮助。然而,假如您想找出权杖是否有个别的群组,可以使用CheckTokenMembership函数。
- Token default DACL 建立物件时,假如您传递NULL给安全性属性时,对找出最近建立物件的DACL很有帮助。
从权杖撷取资讯时,您通常必需呼叫GetTokenInformation,以找出被要求的缓冲器长度,然后再次呼叫它,实际撷取资讯。为了产生真正的容错程序代码,您应该有多次呼叫GetTokenInformation的准备,直到您已经成功地撷取要求的资讯为止。这是因为权杖资讯的大小(例如预设DACL)可以在您呼叫GetTokenInformation撷取缓冲器大小之间,以及您在呼叫GetTokenInformation撷取实际资料时改变。这种多重线程程序设计的条件即是竞赛条件(race condition),而且会导致难以发现的错误,这些错误大约每几个月才出现一次。
以下的函数显示如何正确地呼叫GetTokenInformation,以撷取有关权杖的资讯。它也传回使用LocalAlloc分配之缓冲器里面的权杖资讯。假如您在程序代码中使用了这个函数,则当您用完缓冲器时,应该使用LocalFree释放传回的缓冲器。
LPVOID AllocateTokenInformation(HANDLE hToken,
TOKEN_INFORMATION_CLASS tokenClass ){
PVOID pvBuffer =NULL;
__try{
BOOL fSuccess;
//初始缓冲器大小
ULONG lSize =0 ;
do
{
//我们拥有缓冲器大小了吗?
if (lSize !=0)
{
//我们已经拥有缓冲器吗?
if (pvBuffer !=NULL)
LocalFree(pvBuffer);//Then free it
//分配新的缓冲器
pvBuffer =LocalAlloc(LPTR,lSize);
if(pvBuffer ==NULL)
__leave;
}
//再试一次
fSuccess =GetTokenInformation(hToken,tokenClass,
pvBuffer,lSize,&lSize );
//缓冲器仍旧不够?
}while(!fSuccess &&(GetLastError()==
ERROR_INSUFFICIENT_BUFFER));
//假如为了某些其他原因而失败,退出
if(!fSuccess)
{
if(pvBuffer)
LocalFree(pvBuffer);
pvBuffer =NULL;
}
}__finally{}
//传回本地端分配的缓冲器
return (pvBuffer);
}
以下的程序代码片段,显示使用此函数撷取权杖之使用者SID及当前处理程序的预设DACL。