查找给定的 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 中实现此目的的一系列步骤:

  1. Active DS Type Library 对象集导入 Delphi。这将创建具有所有必要接口的 ActiveDs_TLB 单元
  2. 像这样声明 AdsGetObject

    function ADsGetObject(lpszPathName: WideString; const riid: TGUID; out ppObject): HRESULT; safecall;
    
  3. 使用 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 定义的组。