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