{**************************************************************************************************} { } { Project JEDI Code Library (JCL) } { } { The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); } { you may not use this file except in compliance with the License. You may obtain a copy of the } { License at http://www.mozilla.org/MPL/ } { } { Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF } { ANY KIND, either express or implied. See the License for the specific language governing rights } { and limitations under the License. } { } { The Original Code is JclSecurity.pas. } { } { The Initial Developers of the Original Code are documented in the accompanying help file } { JCLHELP.hlp. Portions created by these individuals are Copyright (C) of these individuals. } { } {**************************************************************************************************} { } { Various NT security related routines to perform commen asks such as enabling and disabling } { privileges. } { } { Unit owner: Peter Friese } { } {**************************************************************************************************} // $Id: JclSecurity.pas,v 1.2 2004/04/14 21:55:07 druid Exp $ unit JclSecurity; {$I jcl.inc} {$IFDEF SUPPORTS_WEAKPACKAGEUNIT} {$WEAKPACKAGEUNIT ON} {$ENDIF SUPPORTS_WEAKPACKAGEUNIT} {$HPPEMIT '#define TTokenInformationClass TOKEN_INFORMATION_CLASS'} interface uses Windows, SysUtils, JclBase; //-------------------------------------------------------------------------------------------------- // Access Control //-------------------------------------------------------------------------------------------------- function AllowRegKeyForEveryone(Key: HKEY; Path: string): Boolean; function CreateNullDacl(var Sa: TSecurityAttributes; const Inheritable: Boolean): PSecurityAttributes; function CreateInheritable(var Sa: TSecurityAttributes): PSecurityAttributes; //-------------------------------------------------------------------------------------------------- // Privileges //-------------------------------------------------------------------------------------------------- function IsAdministrator: Boolean; function EnableProcessPrivilege(const Enable: Boolean; const Privilege: string): Boolean; function EnableThreadPrivilege(const Enable: Boolean; const Privilege: string): Boolean; function IsPrivilegeEnabled(const Privilege: string): Boolean; function GetPrivilegeDisplayName(const PrivilegeName: string): string; function SetUserObjectFullAccess(hUserObject: THandle): Boolean; function GetUserObjectName(hUserObject: THandle): string; //-------------------------------------------------------------------------------------------------- // Account Information //-------------------------------------------------------------------------------------------------- procedure LookupAccountBySid(Sid: PSID; var Name, Domain: string); procedure QueryTokenInformation(Token: THandle; InformationClass: TTokenInformationClass; var Buffer: Pointer); {$IFNDEF FPC} function GetInteractiveUserName: string; {$ENDIF} implementation uses {$IFNDEF FPC} AccCtrl, {$ENDIF} JclStrings, JclSysInfo, JclWin32; //================================================================================================== // Access Control //================================================================================================== function AllowRegKeyForEveryone(Key: HKEY; Path: string): Boolean; var WidePath: PWideChar; Len: Integer; begin case Key of HKEY_LOCAL_MACHINE: Path := 'MACHINE\' + Path; HKEY_CURRENT_USER: Path := 'CURRENT_USER\' + Path; HKEY_CLASSES_ROOT: Path := 'CLASSES_ROOT\' + Path; HKEY_USERS: Path := 'USERS\' + Path; end; Len := (Length(Path)+1)*SizeOf(WideChar); GetMem(WidePath,Len); MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, PChar(Path), -1, WidePath, Len); Result := SetNamedSecurityInfoW(WidePath, SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, nil, nil, nil, nil) = ERROR_SUCCESS; FreeMem(WidePath); end; //-------------------------------------------------------------------------------------------------- function CreateNullDacl(var Sa: TSecurityAttributes; const Inheritable: Boolean): PSecurityAttributes; var Sd: PSecurityDescriptor; begin Sd := AllocMem(SizeOf(TSecurityDescriptor)); Win32Check(InitializeSecurityDescriptor(Sd, SECURITY_DESCRIPTOR_REVISION)); Win32Check(SetSecurityDescriptorDacl(Sd, True, nil, False)); Sa.nLength := SizeOf(Sa); Sa.lpSecurityDescriptor := Sd; Sa.bInheritHandle := Inheritable; Result := @Sa; end; //-------------------------------------------------------------------------------------------------- function CreateInheritable(var Sa: TSecurityAttributes): PSecurityAttributes; begin Sa.nLength := SizeOf(Sa); Sa.lpSecurityDescriptor := nil; Sa.bInheritHandle := True; Result := @Sa; end; //================================================================================================== // Privileges //================================================================================================== function IsAdministrator: Boolean; var psidAdmin: Pointer; Token: THandle; Count: DWORD; TokenInfo: PTokenGroups; HaveToken: Boolean; I: Integer; begin Result := False; psidAdmin := nil; TokenInfo := nil; HaveToken := False; try HaveToken := OpenThreadToken(GetCurrentThread, TOKEN_QUERY, True, Token); if (not HaveToken) and (GetLastError = ERROR_NO_TOKEN) then HaveToken := OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, Token); if HaveToken then begin Win32Check(AllocateAndInitializeSid(SECURITY_NT_AUTHORITY, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, psidAdmin)); {$IFDEF FPC} if GetTokenInformation(Token, TokenGroups, nil, 0, @Count) or (GetLastError <> ERROR_INSUFFICIENT_BUFFER) then RaiseLastOSError; TokenInfo := PTokenGroups(AllocMem(Count)); Win32Check(GetTokenInformation(Token, TokenGroups, TokenInfo, Count, @Count)); {$ELSE FPC} if GetTokenInformation(Token, TokenGroups, nil, 0, Count) or (GetLastError <> ERROR_INSUFFICIENT_BUFFER) then RaiseLastOSError; TokenInfo := PTokenGroups(AllocMem(Count)); Win32Check(GetTokenInformation(Token, TokenGroups, TokenInfo, Count, Count)); {$ENDIF FPC} for I := 0 to TokenInfo^.GroupCount - 1 do begin {$RANGECHECKS OFF} // Groups is an array [0..0] of TSIDAndAttributes, ignore ERangeError Result := EqualSid(psidAdmin, TokenInfo^.Groups[I].Sid); {$IFDEF RANGECHECKS_ON} {$RANGECHECKS ON} {$ENDIF RANGECHECKS_ON} if Result then Break; end; end; finally if TokenInfo <> nil then FreeMem(TokenInfo); if HaveToken then CloseHandle(Token); if psidAdmin <> nil then FreeSid(psidAdmin); end; end; //-------------------------------------------------------------------------------------------------- function EnableProcessPrivilege(const Enable: Boolean; const Privilege: string): Boolean; const PrivAttrs: array [Boolean] of DWORD = (0, SE_PRIVILEGE_ENABLED); var Token: THandle; TokenPriv: TTokenPrivileges; begin Result := False; if OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES, Token) then begin TokenPriv.PrivilegeCount := 1; LookupPrivilegeValue(nil, PChar(Privilege), TokenPriv.Privileges[0].Luid); TokenPriv.Privileges[0].Attributes := PrivAttrs[Enable]; JclWin32.AdjustTokenPrivileges(Token, False, TokenPriv, SizeOf(TokenPriv), nil, nil); Result := GetLastError = ERROR_SUCCESS; CloseHandle(Token); end; end; //-------------------------------------------------------------------------------------------------- function EnableThreadPrivilege(const Enable: Boolean; const Privilege: string): Boolean; const PrivAttrs: array [Boolean] of DWORD = (0, SE_PRIVILEGE_ENABLED); var Token: THandle; TokenPriv: TTokenPrivileges; HaveToken: Boolean; begin Result := False; Token := 0; HaveToken := OpenThreadToken(GetCurrentThread, TOKEN_ADJUST_PRIVILEGES, False, Token); if (not HaveToken) and (GetLastError = ERROR_NO_TOKEN) then HaveToken := OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES, Token); if HaveToken then begin TokenPriv.PrivilegeCount := 1; LookupPrivilegeValue(nil, PChar(Privilege), TokenPriv.Privileges[0].Luid); TokenPriv.Privileges[0].Attributes := PrivAttrs[Enable]; JclWin32.AdjustTokenPrivileges(Token, False, TokenPriv, SizeOf(TokenPriv), nil, nil); Result := GetLastError = ERROR_SUCCESS; CloseHandle(Token); end; end; //-------------------------------------------------------------------------------------------------- function IsPrivilegeEnabled(const Privilege: string): Boolean; var Token: THandle; TokenPriv: TPrivilegeSet; Res: LongBool; HaveToken: Boolean; begin Result := False; Token := 0; HaveToken := OpenThreadToken(GetCurrentThread, TOKEN_QUERY, False, Token); if (not HaveToken) and (GetLastError = ERROR_NO_TOKEN) then HaveToken := OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, Token); if HaveToken then begin TokenPriv.PrivilegeCount := 1; TokenPriv.Control := 0; LookupPrivilegeValue(nil, PChar(Privilege), TokenPriv.Privilege[0].Luid); Result := PrivilegeCheck(Token, TokenPriv, Res) and Res; CloseHandle(Token); end; end; //-------------------------------------------------------------------------------------------------- function GetPrivilegeDisplayName(const PrivilegeName: string): string; var Count: DWORD; LangID: DWORD; begin Count := 0; LangID := 0; // li := DWORD(MAKELANGID(LANG_DEFAULT, LANG_USER)); // have the the API function determine the required string length if not LookupPrivilegeDisplayName(nil, PChar(PrivilegeName), PChar(Result), Count, LangID) then Count := 256; SetLength(Result, Count + 1); if LookupPrivilegeDisplayName(nil, PChar(PrivilegeName), PChar(Result), Count, LangID) then StrResetLength(Result) else Result:= ''; end; //-------------------------------------------------------------------------------------------------- function SetUserObjectFullAccess(hUserObject: THandle): Boolean; var Sd: PSecurity_Descriptor; Si: Security_Information; begin Sd := PSecurity_Descriptor(LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH)); InitializeSecurityDescriptor(Sd, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl(Sd, True, nil, False); Si := DACL_SECURITY_INFORMATION; Result := SetUserObjectSecurity(hUserObject, Si, Sd); LocalFree(HLOCAL(Sd)); end; //-------------------------------------------------------------------------------------------------- function GetUserObjectName(hUserObject: THandle): string; var Count: DWORD; begin // have the the API function determine the required string length GetUserObjectInformation(hUserObject, UOI_NAME, PChar(Result), 0, Count); SetLength(Result, Count + 1); if GetUserObjectInformation(hUserObject, UOI_NAME, PChar(Result), Count, Count) then StrResetLength(Result) else Result := ''; end; //================================================================================================== // Account Information //================================================================================================== procedure LookupAccountBySid(Sid: PSID; var Name, Domain: string); var NameSize, DomainSize: DWORD; Use: SID_NAME_USE; begin NameSize := 0; DomainSize := 0; LookupAccountSid(nil, Sid, nil, NameSize, nil, DomainSize, Use); SetLength(Name, NameSize); SetLength(Domain, DomainSize); Win32Check(LookupAccountSid(nil, Sid, PChar(Name), NameSize, PChar(Domain), DomainSize, Use)); SetLength(Domain, StrLen(PChar(Domain))); SetLength(Name, StrLen(PChar(Name))); end; //-------------------------------------------------------------------------------------------------- procedure QueryTokenInformation(Token: THandle; InformationClass: TTokenInformationClass; var Buffer: Pointer); var B: BOOL; Length, LastError: DWORD; begin Buffer := nil; Length := 0; LastError := 0; {$IFDEF FPC} B := GetTokenInformation(Token, InformationClass, Buffer, Length, @Length); {$ELSE} B := GetTokenInformation(Token, InformationClass, Buffer, Length, Length); {$ENDIF} while (not B) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) do begin ReallocMem(Buffer, Length); {$IFDEF FPC} B := GetTokenInformation(Token, InformationClass, Buffer, Length, @Length); {$ELSE} B := GetTokenInformation(Token, InformationClass, Buffer, Length, Length); {$ENDIF} if not B then LastError := GetLastError; end; if not B then begin FreeMem(Buffer); SetLastError(LastError); RaiseLastOSError; end; end; //-------------------------------------------------------------------------------------------------- {$IFNDEF FPC} // JclSysInfo.GetShellProcessHandle not available function GetInteractiveUserName: string; var Handle: THandle; Token: THandle; User: PTokenUser; Name, Domain: string; begin Handle := GetShellProcessHandle; try Win32Check(OpenProcessToken(Handle, TOKEN_QUERY, Token)); try QueryTokenInformation(Token, TokenUser, Pointer(User)); try LookupAccountBySid(User.User.Sid, Name, Domain); Result := Domain + '\' + Name; finally FreeMem(User); end; finally CloseHandle(Token); end; finally CloseHandle(Handle); end; end; {$ENDIF not FPC} end.