我的程序如何检测它是否在特定域上运行?

时间:2011-01-17 16:37:22

标签: delphi winapi active-directory dns

我需要根据当前登录用户的位置限制应用程序的特定功能。由于我必须在Delphi中实现这个逻辑,所以我不希望过度使用完整的活动目录/ LDAP查询。

我的想法是利用DsGetDcName,并使用DOMAIN_CONTROLLER_INFO结构中返回的GUID,并将其与硬编码常量进行比较。似乎原因是域GUID只会在重新创建域时发生变化,因此这将提供我想要的功能,而且开销有限。我唯一关心的是我在MSDN上找不到任何证明我的假设的文件。

type
  EAccessDenied = Exception;
  EInvalidOwner = Exception;
  EInsufficientBuffer = Exception;
  ELibraryNotFound = Exception;

  NET_API_STATUS = Integer;

  TDomainControllerInfoA = record
    DomainControllerName: LPSTR;
    DomainControllerAddress: LPSTR;
    DomainControllerAddressType: ULONG;
    DomainGuid: TGUID;
    DomainName: LPSTR;
    DnsForestName: LPSTR;
    Flags: ULONG;
    DcSiteName: LPSTR;
    ClientSiteName: LPSTR;
  end;
  PDomainControllerInfoA = ^TDomainControllerInfoA;

const
  NERR_Success = 0;

procedure NetCheck(ErrCode: NET_API_STATUS);
begin
  if ErrCode <> NERR_Success then
  begin
    case ErrCode of
      ERROR_ACCESS_DENIED:
        raise EAccessDenied.Create('Access is Denied');
      ERROR_INVALID_OWNER:
        raise EInvalidOwner.Create('Cannot assign the owner of this object.');
      ERROR_INSUFFICIENT_BUFFER:
        raise EInsufficientBuffer.Create('Buffer passed was too small');
      else
        raise Exception.Create('Error Code: ' + IntToStr(ErrCode) + #13 +
          SysErrorMessage(ErrCode));
    end;
  end;
end;

function IsInternalDomain: Boolean;
var
  NTNetDsGetDcName: function(ComputerName, DomainName: PChar; DomainGuid: PGUID; SiteName: PChar; Flags: ULONG; var DomainControllerInfo: PDomainControllerInfoA): NET_API_STATUS; stdcall;
  NTNetApiBufferFree: function (lpBuffer: Pointer): NET_API_STATUS; stdcall;
  LibHandle: THandle;
  DomainControllerInfo: PDomainControllerInfoA;
  ErrMode: Word;
const
  NTlib = 'NETAPI32.DLL';
  DS_IS_FLAT_NAME = $00010000;
  DS_RETURN_DNS_NAME = $40000000;
  INTERNAL_DOMAIN_GUID: TGUID = '{????????-????-????-????-????????????}';
begin
 if Win32Platform = VER_PLATFORM_WIN32_NT then
    begin
    ErrMode := SetErrorMode(SEM_NOOPENFILEERRORBOX);
    LibHandle := LoadLibrary(NTlib);
    SetErrorMode(ErrMode);
    if LibHandle = 0 then
        raise ELibraryNotFound.Create('Unable to map library: ' + NTlib);
    try
      @NTNetDsGetDcName := GetProcAddress(Libhandle, 'DsGetDcNameA');
      @NTNetApiBufferFree       := GetProcAddress(Libhandle,'NetApiBufferFree');
      try
        NetCheck(NTNetDsGetDcName(nil, nil, nil, nil, DS_IS_FLAT_NAME or DS_RETURN_DNS_NAME, DomainControllerInfo));
        Result := (DomainControllerInfo.DomainName = 'foo.com') and (CompareMem(@DomainControllerInfo.DomainGuid,@INTERNAL_DOMAIN_GUID, SizeOf(TGuid)));//WideCharToString(pDomain);
      finally
        NetCheck(NTNetApiBufferFree(DomainControllerInfo));
      end;
    finally
      FreeLibrary(LibHandle);
    end;
    end
 else
  Result := False;
end;

按照建议在ServerFault添加了相关问题。

Technet上发现了另一个有趣的读物,似乎也暗示我是正确的,但不是专门针对域SID的。

3 个答案:

答案 0 :(得分:2)

在域上创建服务帐户;

获取服务帐户的GUID并对其进行加密并将其保存在某处(注册表)可能作为企业安装过程的一部分来验证许可协议。

启动域服务帐户GUID的客户端应用程序查询并使用保存的GUID对其进行验证。

或者创建自己的企业“密钥”服务器。

执行LDAP查询比执行所有域控制器垃圾更容易。

答案 1 :(得分:1)

如果我理解您的要求,您案例中最好的API是GetUserNameEx。您可以选择EXTENDED_NAME_FORMAT类型的NameFormat参数值,以便更好地验证。如果您想要另外验证有关运行程序的计算机的信息,则另一个函数GetComputerNameEx会很有用。

答案 2 :(得分:0)

  

我需要限制具体的   基于的应用程序的功能   当前记录的位置   在用户

如果您要查找当前登录的 用户 的位置,则不应使用 DsGetDcName

您的计算机可以加入 domainA 。您的登录用户可以来自 domainB 。在您的计算机上调用 DsGetDcName 并不会为您提供 domainB GUID ,但它会为您提供 domainA GUID

因此,我认为您应该使用LookupAccountName代替。 LookupAccountName 为您提供当前登录用户的SID。然后,您可以从用户SID中提取域SID。该域SID实际上是该用户来自的域。有关如何从用户SID提取域SID的详细信息,请查看here

关于您对域GUID唯一性的原始问题,很抱歉我没有答案。 AFAIK,没有可用的工具允许您更改域SID和GUID。我不确定如何难以入侵并改变它。