查找给定的 SID 是否属于 SID 标识的组
Find if a given SID belongs to a group identified by SID
我正在编写一个 windows 服务,它根据不同的规则执行操作,其中之一是基于请求用户身份。
它因此接收请求的用户 SID,然后将其与其内部 SID 列表进行比较,以确定它将执行的操作。使用 EqualSID
API 函数使这变得非常简单。
但是,我现在面临的情况是,服务列表中的某些SID 是组SID 而不是用户SID。
这意味着我必须找到一种方法来测试接收到的 SID 是否等于列表中的 SID 或是否属于列表中 SID 所代表的组。
我环顾四周,看看有哪些 API 可用,发现 CheckTokenMembership
需要令牌句柄。这就是我有点迷路的地方,因为服务不一定位于同一台机器上,我似乎无法找到一种方法来从我收到的 SID 创建有效的令牌句柄。
该服务本身在默认 "NT Service" 帐户下运行,我希望它能保持这种状态。
你建议我使用什么API?
目标语言是Delphi,但我能看懂普通C语言的例子。
好吧,在查看了其他各种东西之后,我终于设法找到了实现这一目标的方法。简而言之,答案是 Active Directory Service Interfaces
也称为 ADSI
如果其他人正在查看,请提供更多详细信息,这里是 Delphi 中实现此目的的一系列步骤:
- 将
Active DS Type Library
对象集导入 Delphi。这将创建具有所有必要接口的 ActiveDs_TLB
单元
像这样声明 AdsGetObject
function ADsGetObject(lpszPathName: WideString; const riid: TGUID; out ppObject): HRESULT; safecall;
使用 SID 语法检索 IADSUser 实例:
var
SIDUser: IADSUser;
User: IADSUser;
SIDGroup: IADSGroup;
Group: IADSGroup;
begin
// Bind using the SID
AdsGetObject('LDAP://<SID=S-1-5-7>', IADSUser, SIDUser);
// rebind using the distinguished name as suggested by MSDN
AdsGetObject('LDAP://' + SIDUser.Get('distinguishedName'), IADSUser, User);
// Use the User instance
ShowMessage(User.FullName);
// Same method for group
AdsGetObject('LDAP://<SID=S-1-5-32-545>', IADSGroup, SIDGroup);
AdsGetObject('LDAP://' + SIDGroup.Get('distinguishedName'), IADSGroup, Group);
// IsMember does not seem to work with LDAP
// https://groups.google.com/forum/#!topic/microsoft.public.adsi.general/2d-e4HPXGfA
// http://www.rlmueller.net/Programs/IsMember4.txt
// if Group.IsMember(User.ADsPath) then
if AdsIsMember(User, 'S-1-5-32-545') then
ShowMessage('InGroup');
end;
如您所见,人们可能想使用 IADSGroup 的 IsMember 方法,但显然它不起作用,因为在上述情况下它应该 return True(S-1-5-32-545
是世界组) .
因此,正如 link 在评论中给出的建议,我这样写了自己的 IsMember:
function ADsIsMember(const User: IADSUser; const GroupSID: string): Boolean;
const
TokenGroupsId = 'tokenGroups';
var
PropNames: array of OleVariant;
TokenGroups: OleVariant;
TokenGroupLow: Integer;
TokenGroupHigh: Integer;
TokenGroupIndex: Integer;
SIDBytes: array of Byte;
SIDAsString: PChar;
begin
Result := False;
SetLength(PropNames, 1);
PropNames[0] := TokenGroupsId;
User.GetInfoEx(PropNames, 0);
TokenGroups := User.Get(TokenGroupsId);
TokenGroupLow := VarArrayLowBound(TokenGroups, 1);
TokenGroupHigh := VarArrayHighBound(TokenGroups, 1);
for TokenGroupIndex := TokenGroupLow to TokenGroupHigh do
begin
SIDBytes := TokenGroups[TokenGroupIndex];
ConvertSidToStringSid(@SIDBytes[0], SIDAsString);
if GroupSID = SIDAsString then
Exit(True);
end;
end;
有了这些,我现在可以检查给定的 SID 是否属于由其 SID 定义的组。
我正在编写一个 windows 服务,它根据不同的规则执行操作,其中之一是基于请求用户身份。
它因此接收请求的用户 SID,然后将其与其内部 SID 列表进行比较,以确定它将执行的操作。使用 EqualSID
API 函数使这变得非常简单。
但是,我现在面临的情况是,服务列表中的某些SID 是组SID 而不是用户SID。
这意味着我必须找到一种方法来测试接收到的 SID 是否等于列表中的 SID 或是否属于列表中 SID 所代表的组。
我环顾四周,看看有哪些 API 可用,发现 CheckTokenMembership
需要令牌句柄。这就是我有点迷路的地方,因为服务不一定位于同一台机器上,我似乎无法找到一种方法来从我收到的 SID 创建有效的令牌句柄。
该服务本身在默认 "NT Service" 帐户下运行,我希望它能保持这种状态。
你建议我使用什么API?
目标语言是Delphi,但我能看懂普通C语言的例子。
好吧,在查看了其他各种东西之后,我终于设法找到了实现这一目标的方法。简而言之,答案是 Active Directory Service Interfaces
也称为 ADSI
如果其他人正在查看,请提供更多详细信息,这里是 Delphi 中实现此目的的一系列步骤:
- 将
Active DS Type Library
对象集导入 Delphi。这将创建具有所有必要接口的ActiveDs_TLB
单元 像这样声明 AdsGetObject
function ADsGetObject(lpszPathName: WideString; const riid: TGUID; out ppObject): HRESULT; safecall;
使用 SID 语法检索 IADSUser 实例:
var SIDUser: IADSUser; User: IADSUser; SIDGroup: IADSGroup; Group: IADSGroup; begin // Bind using the SID AdsGetObject('LDAP://<SID=S-1-5-7>', IADSUser, SIDUser); // rebind using the distinguished name as suggested by MSDN AdsGetObject('LDAP://' + SIDUser.Get('distinguishedName'), IADSUser, User); // Use the User instance ShowMessage(User.FullName); // Same method for group AdsGetObject('LDAP://<SID=S-1-5-32-545>', IADSGroup, SIDGroup); AdsGetObject('LDAP://' + SIDGroup.Get('distinguishedName'), IADSGroup, Group); // IsMember does not seem to work with LDAP // https://groups.google.com/forum/#!topic/microsoft.public.adsi.general/2d-e4HPXGfA // http://www.rlmueller.net/Programs/IsMember4.txt // if Group.IsMember(User.ADsPath) then if AdsIsMember(User, 'S-1-5-32-545') then ShowMessage('InGroup'); end;
如您所见,人们可能想使用 IADSGroup 的 IsMember 方法,但显然它不起作用,因为在上述情况下它应该 return True(S-1-5-32-545
是世界组) .
因此,正如 link 在评论中给出的建议,我这样写了自己的 IsMember:
function ADsIsMember(const User: IADSUser; const GroupSID: string): Boolean;
const
TokenGroupsId = 'tokenGroups';
var
PropNames: array of OleVariant;
TokenGroups: OleVariant;
TokenGroupLow: Integer;
TokenGroupHigh: Integer;
TokenGroupIndex: Integer;
SIDBytes: array of Byte;
SIDAsString: PChar;
begin
Result := False;
SetLength(PropNames, 1);
PropNames[0] := TokenGroupsId;
User.GetInfoEx(PropNames, 0);
TokenGroups := User.Get(TokenGroupsId);
TokenGroupLow := VarArrayLowBound(TokenGroups, 1);
TokenGroupHigh := VarArrayHighBound(TokenGroups, 1);
for TokenGroupIndex := TokenGroupLow to TokenGroupHigh do
begin
SIDBytes := TokenGroups[TokenGroupIndex];
ConvertSidToStringSid(@SIDBytes[0], SIDAsString);
if GroupSID = SIDAsString then
Exit(True);
end;
end;
有了这些,我现在可以检查给定的 SID 是否属于由其 SID 定义的组。