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.
精彩评论