开发者

Checking for Administrator user login in non-English installations of windows

I have some small questions...I have a program that stores a list of users in a database and compares on program startup if the user is in the list or is an administrator before letting them use it. At the moment, the way I'm using to check if the user is an administrator is simply by comparing the username to a string constant called 'ADMINISTRATOR'. Will this work on a non-Engish system? I.E. does Windows use a language specific version of 'administrator'? Or maybe is there an enumerated version of the Admin user t开发者_JS百科hat I can use to check with instead of my 'ADMINISTRATOR' string? (you know, just like how Windows folders are enumerated). I'm using Delphi 2009 by the way. Thanks in advance!


No, don't do it that way. It will surely break. You could get a list of all the groups the user is a member of and check if one of the SIDs is S-1-5-32-544, which is the SID of the Administrators group. There is a list of well known SIDs. There is also an SID for the original administrator account.

Here is the list:

http://support.microsoft.com/kb/243330


NEWS

In 2010, @ChristianWimmer criticized my coding style. Now, two years afterward, I have to use the function again in my program. So, I decided to improve the coding style of the function.

Overview

I pick out a small portion of my private library for your convenience. To test whether user account of the access token is a member of the local administrator's group, pass WinBuiltinAdministratorsSid of JwaWinNT to eWellKnownSidType parameter. Note, it requires JEDI API Libray because Delphi Windows.pas unit didn't define CreateWellKnownSid().

Implementation

//------------------------------------------------------------------------------
// Purpose: Tests whether user account of the access token is a member of the
//   specified well known group, and report its elevation type.
// Parameter:
//   hToken [in,opt]
//     A handle to an access token having TOKEN_QUERY and TOKEN_DUPLICATE
//     access. If hToken is 0: if it is an impersonation token, the access token
//     of the calling thread is used; otherwise, the access token associated
//     with the process is used.
//   eWellKnownSidType [in]
//     Member of the WELL_KNOWN_SID_TYPE enumeration that specifies what Sid the
//     function will identify.
//   pDomainSid [in,opt]
//     A pointer to a SID that identifies the domain to use when identifying the
//     Sid. Pass nil to use the local computer.
//   peElevType [out,opt]
//     A pointer to a variable that receives the following elevation type of the
//     access token:
//       - TokenElevationTypeDefault: The access token does not have a linked
//         token. This value is reported under Windows prior to Windows Vista.
//       - TokenElevationTypeFull: The access token is an elevated token.
//       - TokenElevationTypeLimited: The access token is a limited token.
// Return value:
//   - True if user account of the access token is a member of the well known
//     group specified in eWellKnownSidType parameter.
//   - False, otherwise. To get error information, call GetLastError().
// Remarks:
//   To test whether user account of the access token is a member of local
//   administrators group, pass JwaWinNT.WinBuiltinAdministratorsSid to
//   eWellKnownSidType parameter.
// References:
//   - How To Determine Whether a Thread Is Running in User Context of
//     Local Administrator Account [MSDN]
//------------------------------------------------------------------------------
function Inu_IsMemberOfWellKnownGroup(const hToken: Windows.THandle;
    const eWellKnownSidType: JwaWinNT.WELL_KNOWN_SID_TYPE;
    const pDomainSid: JwaWinNT.PSID=nil;
    peElevType: PTokenElevationType=nil): Boolean;
var
  hAccessToken: Windows.THandle;
  rOSVerInfo: Windows.OSVERSIONINFO;
  eTET: Windows.TTokenElevationType;
  iReturnLen: Windows.DWORD;
  hTokenToCheck: Windows.THandle;
  iSidLen: Windows.DWORD;
  pGroupSid: JwaWinNT.PSID;
  bMemberOfWellKnownGroup: Windows.BOOL;
begin
  Result := False;
  hAccessToken := 0;
  hTokenToCheck := 0;
  pGroupSid := nil;
  try
    if hToken = 0 then begin // If the caller doesn't supply a token handle,
      // Get the calling thread's access token
      if not Windows.OpenThreadToken(Windows.GetCurrentThread(),
          Windows.TOKEN_QUERY or Windows.TOKEN_DUPLICATE,
          True, hAccessToken) then begin
        if Windows.GetLastError() <> Windows.ERROR_NO_TOKEN then
          Exit();
        // If no thread token exists, retry against process token
        if not Windows.OpenProcessToken(Windows.GetCurrentProcess(),
            Windows.TOKEN_QUERY or Windows.TOKEN_DUPLICATE, hAccessToken) then
          Exit();
      end;
    end
    else // If the caller supplies a token handle,
      hAccessToken := hToken;
    // Determine whether the system is running Windows Vista or later because
    // because they support linked tokens, previous versions don't.
    rOSVerInfo.dwOSVersionInfoSize := SizeOf(Windows.OSVERSIONINFO);
    if not Windows.GetVersionEx(rOSVerInfo) then
      Exit();
    if rOSVerInfo.dwMajorVersion >= 6 then begin
      // Retrieve information about the elevation level of the access token
      if not Windows.GetTokenInformation(hAccessToken,
          Windows.TokenElevationType, @eTET,
          SizeOf(Windows.TTokenElevationType), iReturnLen) then
        Exit();
      // If the access token is a limited token, retrieve the linked token
      // information from the access token.
      if eTET = Windows.TokenElevationTypeLimited then begin
        if not Windows.GetTokenInformation(hAccessToken,
            Windows.TokenLinkedToken, @hTokenToCheck,
            SizeOf(Windows.TTokenLinkedToken), iReturnLen) then
          Exit();
      end;
      // Report the elevation type if it is wanted
      if Assigned(peElevType) then
        peElevType^ := eTET;
    end
    else begin // if rOSVerInfo.dwMajorVersion < 6
      // There is no concept of elevation prior to Windows Vista
      if Assigned(peElevType) then
        peElevType^ := Windows.TokenElevationTypeDefault;
    end;
    // CheckTokenMembership() requires an impersonation token. If we just got a
    // linked token, it is already an impersonation token. Otherwise, duplicate
    // the original as an impersonation token for CheckTokenMembership().
    if (hTokenToCheck = 0) and (not Windows.DuplicateToken(hAccessToken,
        Windows.SecurityIdentification, @hTokenToCheck)) then
      Exit();
    // Allocate enough memory for the longest possible Sid
    iSidLen := JwaWinNT.SECURITY_MAX_SID_SIZE;
    pGroupSid := JwaWinNT.PSid(Windows.LocalAlloc(Windows.LMEM_FIXED, iSidLen));
    if not Assigned(pGroupSid) then
      Exit();
    // Create a Sid for the predefined alias as specified in eWellKnownSidType
    if not JwaWinBase.CreateWellKnownSid(eWellKnownSidType, pDomainSid,
        pGroupSid, iSidLen) then
      Exit();
    // Now, check presence of the created Sid in the user and group Sids of the
    // access token. In other words, it determines whether the user is a member
    // of the well known group specified in eWellKnownSidType parameter.
    if not JwaWinBase.CheckTokenMembership(hTokenToCheck, pGroupSid,
        bMemberOfWellKnownGroup) then
      Exit();
    Result := bMemberOfWellKnownGroup;
  finally
    // Close the access token handle
    if hAccessToken <> 0 then
      Windows.CloseHandle(hAccessToken);
    // Close the new duplicate token handle if exists
    if (hTokenToCheck <> 0) then
      Windows.CloseHandle(hTokenToCheck);
    // Free the allocated memory for the Sid created by CreateWellKnownSid()
    if Assigned(pGroupSid) then
      Windows.LocalFree(Windows.HLOCAL(pGroupSid));
  end;
end; // endfunction Inu_IsMemberOfWellKnownGroup
//==============================================================================


This is an excerpt from JwsclToken.pas from the JEDI API&WSCL. Both functions do the same check but in different ways. You see how little code is used? The same code in plain WinAPI would be at least 5 times bigger. Of course you can just call these functions from the unit itself. No need to copy here!

function JwCheckAdministratorAccess: boolean;
var
  SD: TJwSecurityDescriptor;
begin
  if not Assigned(JwAdministratorsSID) then
    JwInitWellKnownSIDs;

  SD := TJwSecurityDescriptor.Create;
  try
    SD.PrimaryGroup := JwNullSID;
    SD.Owner   := JwAdministratorsSID;
    SD.OwnDACL := True;

    SD.DACL.Add(TJwDiscretionaryAccessControlEntryAllow.Create(nil,
      [], STANDARD_RIGHTS_ALL, JwAdministratorsSID, False));

    Result := TJwSecureGeneralObject.AccessCheck(SD, nil,
      STANDARD_RIGHTS_ALL, TJwSecurityGenericMapping);
  finally
    FreeAndNil(SD);
  end;
end;

function JwIsMemberOfAdministratorsGroup: boolean;
var
  Token: TJwSecurityToken;
begin
  Token := TJwSecurityToken.CreateTokenEffective(TOKEN_READ or
    TOKEN_DUPLICATE);
  try
    Token.ConvertToImpersonatedToken(SecurityImpersonation, MAXIMUM_ALLOWED);
    Result := Token.CheckTokenMembership(JwAdministratorsSID)
  finally
    FreeAndNil(Token);
  end;
end;


It varies from windows version to windows version... in pre-vista... administrator username is in the primary windows language... for example, in spanish it is Administrador.

In post-vista, there's no administrator user. You shall store and check for user privileges.

I found this IsAdmin function and you may find it useful too...


There is the CreateWellKnownSid function.

But explicit check for admin account may be not a good idea. Just do the operation and ask for elevation, if you got 'access denied' error.

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜