开发者

How can my program detect whether it's running on a particular domain?

开发者 https://www.devze.com 2023-02-05 16:17 出处:网络
I have the need to restrict specific functions of an application based on the location of the currently logged in user. As I have to implement this logic in Delphi, I\'d prefer not to go overboard wit

I have the need to restrict specific functions of an application based on the location of the currently logged in user. As I have to implement this logic in Delphi, I'd prefer not to go overboard with full active directory/LDAP queries.

My curent thought is to utilize DsGetDcName, and use the GUID returned in the DOMAIN_CONTROLLER_INFO structure and compare it to a hard coded constant. It seems to reason that a domain GUID would only change if the domain is recreated, so this would provide functionality that I desire with limited overhead. My only concern is that I can't find any documentation on MSDN confirming my assumption.

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;

Added a related question on ServerFault as suggested.

Found another interesting read on Technet which also seems to hint at me being right, but isn't specifically scoped at domain SID's.


Create a service account on the domain;

Get the GUID of the service account and encrypt it and save it somewhere (registry) maybe as part of enterprise install process to validate a license agreement.

On startup of the client app query for the Domain Service Account GUID and validate it with the saved GUID.

Or create your own enterprise 'key' server.

Doing an LDAP query is easier than doing all the domain controller crap.


If I correct understand your requirement the best API in your case is GetUserNameEx. You can choose the value of NameFormat parameter of the type EXTENDED_NAME_FORMAT which you can better verify. Another function GetComputerNameEx is helpful if you want additionally verify the information about the computer where the program is running.


I have the need to restrict specific functions of an application based on the location of the currently logged in user

If you are trying to find out the location of the currently logged in user, you shouldn't be using DsGetDcName.

Your computer can be joined to domainA. Your logon user can be from domainB. Calling DsGetDcName on your computer doesn't give you domainB GUID but it will give you domainA GUID

Therefore, I think you should use LookupAccountName instead. The LookupAccountName gives you the currently logged in user's SID. Then, you can extract the domain SID from the user SID. That domain SID is really the domain where this user coming from. For the details of how to extract a domain SID from a user SID, please check here

Regarding to your original question about the uniqueness of the domain GUID, I am sorry that I don't have answer on it. AFAIK, there is no tool available allowing you to change the domain SID nor the GUID. I am not sure how hard to hack into it and change it.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号