11 Mar
Posted by: Christian Wimmer in: JEDI Windows API Headers, JEDI Windows Security Code Lib
This article is about how to retrieve the owner of a file. If you are experienced with some of the WinAPI security function this can be pretty easy. There are some problems that needs to be addressed though. The first one is the size of the security items like the SID name of the owner. Secondly, there is the possibility that a SID cannot be resolved to a human readable name at all. And thirdly, we need to check all the result values.
uses
SysUtils,
//JwaWindows, //Use either JwaWindows or these JEDI headers:
JwaWinBase,
JwaWinType,
JwaWinNT,
JwaAclApi,
JwaAccCtrl,
JwaWinError,
JwaSddl;
type
TOwnerResult = (orNone, orName, orSID);
function GetFileOwner(const FileName: string;
out Domain, Username: String): TOwnerResult;
var
pSD: PSecurityDescriptor;
dwOwnerNameSize, dwDomainNameSize: DWORD;
pOwnerSID: PSID;
pszOwnerName, pszDomainName: PChar;
OwnerType: SID_NAME_USE;
dwError : DWORD;
begin
result := orNone;
pSD := nil;
dwError := GetNamedSecurityInfo(
PChar(FileName),//__in LPTSTR pObjectName,
SE_FILE_OBJECT,//__in SE_OBJECT_TYPE ObjectType,
OWNER_SECURITY_INFORMATION,//__in SECURITY_INFORMATION SecurityInfo,
@pOwnerSID,//__out_opt PSID *ppsidOwner,
nil,//__out_opt PSID *ppsidGroup,
nil,//__out_opt PACL *ppDacl,
nil,//__out_opt PACL *ppSacl,
pSD//__out_opt PSECURITY_DESCRIPTOR *ppSecurityDescriptor
);
if (dwError <> NOERROR) then
begin
SetLastError(dwError);
RaiseLastOSError;
end;
//First get necessary memory size for Owner and Domain
dwOwnerNameSize := 0;
dwDomainNameSize := 0;
if not LookupAccountSID(nil, pOwnerSID, nil,
dwOwnerNameSize, nil, dwDomainNameSize, OwnerType) then
begin
//Check for SIDs that are unknown on this local system
if (GetLastError() = ERROR_NONE_MAPPED) then
begin
Win32Check(ConvertSidToStringSid(pOwnerSID, pszOwnerName));
Domain := '';
Username := pszOwnerName;
LocalFree(HLOCAL(pszOwnerName));
result := orSID;
exit;
end
else
//Any other error exits the function
if (GetLastError() <> ERROR_INSUFFICIENT_BUFFER) then
RaiseLastOsError;
end;
//Allocate memory for owner and domain
//Take into account the size of a char type (like WideCHAR)
GetMem(pszOwnerName, dwOwnerNameSize*sizeof(CHAR));
try
GetMem(pszDomainName, dwDomainNameSize*sizeof(CHAR));
try
//Retrieve name and domain
Win32Check(LookupAccountSID(nil, pOwnerSID, pszOwnerName,
dwOwnerNameSize, pszDomainName, dwDomainNameSize, OwnerType));
result := orName;
Domain := pszDomainName;
Username := pszOwnerName;
finally
FreeMem(pszDomainName);
end;
finally
FreeMem(pszOwnerName);
end;
end;
The function behaves the following:
Of course, JWSCL wraps these function calls already. Thus the source size decreases rapidly. See another example here.
In JWSCL it is quite different to implement the function. However the behaviour is equal:
function JwsclGetFileOwner(const FileName: string;
out Domain, Username: String): TOwnerResult;
var
F : TJwSecureFileObject;
Owner : TJwSecurityId;
begin
F := TJwSecureFileObject.Create(FileName);
try
Owner := F.Owner; //Owner is cached/freed by TJwSecureFileObject
try
Domain := Owner.GetAccountDomainName();
Username := Owner.GetAccountName();
Result := orName;
except
on E : EJwsclWinCallFailedException do
begin
if E.LastError = ERROR_NONE_MAPPED then
begin
Domain := '';
Username := Owner.StringSID;
Result := orSID;
end
else
raise;
end;
end;
finally
F.Free;
end;
end;
In a next version of JWSCL (currently trunk) the exception handling will be a little bit different:
function JwsclGetFileOwner2(const FileName: string;
out Domain, Username: String): TOwnerResult;
var
F : TJwSecureFileObject;
Owner : TJwSecurityId;
begin
F := TJwSecureFileObject.Create(FileName);
try
Owner := F.Owner; //Owner is cached/freed by TJwSecureFileObject
try
Domain := Owner.GetAccountDomainName();
Username := Owner.GetAccountName();
Result := orName;
except
on E : EJwsclSidNotMappedException do //not mapped error is handled differently
begin
Domain := '';
Username := Owner.StringSID;
Result := orSID;
end;
end;
finally
F.Free;
end;
end;
If you use JWSCL (Version >= 0.9.2a) and enable the compiler switch JWSCL_SIDCACHE in file Include\Jwscl.inc, JWSCL will add a SID cache (for all TJwSecurityID instances globally). In this way LookupAccountSid will be called only once for every unknown SID and thus SIDs that were already translated are retrieved from cache instead.
Leave a reply
You must be logged in to post a comment.