Page 1 of 1
Forum

Welcome to the Tweaking4All community forums!
When participating, please keep the Forum Rules in mind!

Topics for particular software or systems: Start your topic link with the name of the application or system.
For example “MacOS X – Your question“, or “MS Word – Your Tip or Trick“.

Please note that switching to another language when reading a post will not bring you to the same post, in Dutch, as there is no translation for that post!



Share:
Notifications
Clear all

[Solved] Lazarus - macOS - Request Admin Authentication

37 Posts
2 Users
14 Reactions
14.1 K Views
(@Anonymous)
Joined: 1 second ago
Posts: 0
 

For the avoidance of doubt:

unit Unit1;

{$mode objfpc}{$H+}
{$modeswitch objectivec1}
{$linkframework Security}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, MacOSAll, BaseUnix;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private

  public

  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
var
  status:OSStatus;
  authRef: AuthorizationRef;
  authFlags: AuthorizationFlags;
  authRights: AuthorizationRights;
  authItem: AuthorizationItem;
  pipe: UnivPtr;
  arguments: Arg10000TypePtr;
begin
  authItem.flags := 0;
  authItem.name  := kAuthorizationRightExecute;
  authItem.value := nil;
  authItem.valueLength:= 0;

  authRights.count := 1;
  authRights.items := @authItem;

  authRef   := nil;
  authFlags := kAuthorizationFlagInteractionAllowed or kAuthorizationFlagExtendRights or kAuthorizationFlagPreAuthorize;
  status    := AuthorizationCreate(@authRights, kAuthorizationEmptyEnvironment, authFlags, authRef);

  arguments := nil;
  pipe      := nil;

  if status=errAuthorizationSuccess then
    status := AuthorizationExecuteWithPrivileges(authRef,PChar(ParamStr(0)){'/bin/mkdir'},kAuthorizationFlagDefaults,arguments,pipe);

  ShowMessage(BoolToStr(status=errAuthorizationSuccess,'OK','FAIL')+' '+syserrormessage({IntToStr(}status));

  AuthorizationFree(authRef, kAuthorizationFlagDefaults);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  ForceDirectories('/Library/Application Support/My Custom Path/');
  if DirectoryExists('/Library/Application Support/My Custom Path/') then
  begin
    Caption := 'mkay';
    RemoveDir('/Library/Application Support/My Custom Path/');
  end
  else
    Caption := 'fail';
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  if FpGeteuid = 0 then Caption := 'elevator'
    else Caption := 'piss poor';
end;

end.

   
ReplyQuote
(@Anonymous)
Joined: 1 second ago
Posts: 0
 

To recap:

1. The deprecated method still works.

2. It only seems to run into issues passing command line parameters. How do we fix that (still need to pass a few)? Maybe is that the environment stuff in the initial authorization request, then?

3. At this point I am fully certain this is how VMware InstallBuilder does it too.

Thank you so much for all your help, as we now have a very elegant, albeit deprecated, solution to the problem.


   
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 12 years ago
Posts: 2864
Topic starter  
Posted by: @johngaver

So bringing our own copy of osascript: Is this file not versioned, doesn't it have dependencies on OS libraries which might be version sensitive (just thinking about how things happen in olden Windows land), and thus wouldn't it be dangerous to bundle it? Apple does have reasonably decent SIP though (unless manually disabled of course), so I suppose based on your answer to the preceding, it might be better to just invoke it rather than to bundle it.

That risk does exist indeed ... though the binary has been signed by Apple, so it may turn out to be OK.
Again: not the correct way to do things, and it feels a little hacky 😉 

Posted by: @johngaver

Apps being unable to elevate themselves - OK, so how about we elevate some other helper-ish app, which then just reverse invokes its own elevator in now-elevated mode? That might fly, no?

Probably won't fly. What you could do (or maybe this is what you already meant to say): have a binary "A" start and stay invisible (very easy to do with Lazarus), which then calls "B" with elevated rights.

Posted by: @johngaver

Oh wow seriously? What happens if I pass ParamStr(0) instead?

It works, yaaay!

Yeah, it was early in the morning (for me) and I knew it would be something simple to fix the error (as I had mentioned: it failed for me as well, just needed a minor tweak).

A few things on this line that fails:

status := AuthorizationExecuteWithPrivileges(authRef,'/bin/mkdir "/Library/Application Support/Banana"',kAuthorizationFlagDefaults,arguments,pipe);

 

The "pipe" parameter can be used to catch the exit code or output text of the command you were calling (if I understood things right) - see the ObjC example.

The parameter "arguments" should be (source):

An argv-style vector of strings (array of null-terminated char * pointers ending with a NULL pointer) to send to the tool. 
(You do not need to pass the name of the command as the first argument.)

 

So this is where I should have passed the path, but it was early and I'm a little rusty on the Apple sauce mix here 😉 
(arguments needs to be a pointer, to an array of zero or more pointers, that each points to a parameter you'd like to pass)

That should do the trick.


   
johngaver reacted
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 12 years ago
Posts: 2864
Topic starter  
Posted by: @johngaver

Thank you so much for all your help, as we now have a very elegant, albeit deprecated, solution to the problem.

I have no idea who long Apple can or will keep this in their OS, but I think the reason it's still there is because the alternative kind-a stinks for this type of purpose. 😜 
I read somewhere it was deprecated even before 2011 already and it is still here ...

I agree: VMWare probably uses this as well, and probably most installers do.

Posted by: @johngaver

Thank you so much for all your help

You're most welcome 😊 


   
johngaver reacted
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 12 years ago
Posts: 2864
Topic starter  

I couldn't let it go ... haha ... so with a little tinkering (I'm sure this can be done nicer and maybe even more correct), but ...

This works - it creates the directory. 😊 

Now you can pass multiple parameters of course, and ... you only need to run "AuthorizationCreate" only once since you can reuse the authRef for multiple calls to "AuthorizationExecuteWithPrivileges".
I'm sure you can pass ParamStr just fine here as well. In the same way.

procedure TForm1.Button1Click(Sender: TObject);
var
  status:OSStatus;
  authRef: AuthorizationRef;
  authFlags: AuthorizationFlags;
  authRights: AuthorizationRights;
  authItem: AuthorizationItem;
  pipe: UnivPtr;
  oneargument: string;
  arguments: Arg10000Type;
  parguments: Arg10000TypePtr;
begin
  authItem.flags := 0;
  authItem.name  := kAuthorizationRightExecute;
  authItem.value := nil;
  authItem.valueLength:= 0;

  authRights.count := 1;
  authRights.items := @authItem;

  authRef   := nil;
  authFlags := kAuthorizationFlagInteractionAllowed or kAuthorizationFlagExtendRights or kAuthorizationFlagPreAuthorize;
  status    := AuthorizationCreate(@authRights, kAuthorizationEmptyEnvironment, authFlags, authRef);

  oneargument := '/Library/Application support/GoofyDir';
  parguments  := @arguments;
  arguments[0]:= PChar(oneargument);


  pipe      := nil;

  if status=errAuthorizationSuccess then
    status := AuthorizationExecuteWithPrivileges(authRef,'/bin/mkdir',kAuthorizationFlagDefaults,parguments,pipe);

  ShowMessage(BoolToStr(status=errAuthorizationSuccess,'OK','FAIL')+' '+syserrormessage({IntToStr(}status));

  AuthorizationFree(authRef, kAuthorizationFlagDefaults);
end;     

   
johngaver reacted
ReplyQuote
(@Anonymous)
Joined: 1 second ago
Posts: 0
 
Posted by: @hans
Posted by: @johngaver

So bringing our own copy of osascript: Is this file not versioned, doesn't it have dependencies on OS libraries which might be version sensitive (just thinking about how things happen in olden Windows land), and thus wouldn't it be dangerous to bundle it? Apple does have reasonably decent SIP though (unless manually disabled of course), so I suppose based on your answer to the preceding, it might be better to just invoke it rather than to bundle it.

That risk does exist indeed ... though the binary has been signed by Apple, so it may turn out to be OK.
Again: not the correct way to do things, and it feels a little hacky 😉 

Posted by: @johngaver

Apps being unable to elevate themselves - OK, so how about we elevate some other helper-ish app, which then just reverse invokes its own elevator in now-elevated mode? That might fly, no?

Probably won't fly. What you could do (or maybe this is what you already meant to say): have a binary "A" start and stay invisible (very easy to do with Lazarus), which then calls "B" with elevated rights.

Posted by: @johngaver

Oh wow seriously? What happens if I pass ParamStr(0) instead?

It works, yaaay!

Yeah, it was early in the morning (for me) and I knew it would be something simple to fix the error (as I had mentioned: it failed for me as well, just needed a minor tweak).

A few things on this line that fails:

status := AuthorizationExecuteWithPrivileges(authRef,'/bin/mkdir "/Library/Application Support/Banana"',kAuthorizationFlagDefaults,arguments,pipe);

 

The "pipe" parameter can be used to catch the exit code or output text of the command you were calling (if I understood things right) - see the ObjC example.

The parameter "arguments" should be (source):

An argv-style vector of strings (array of null-terminated char * pointers ending with a NULL pointer) to send to the tool. 
(You do not need to pass the name of the command as the first argument.)

 

So this is where I should have passed the path, but it was early and I'm a little rusty on the Apple sauce mix here 😉 
(arguments needs to be a pointer, to an array of zero or more pointers, that each points to a parameter you'd like to pass)

That should do the trick.

Wow, a pointer to an array of pointers - how fun!

Any chance you might be able to post a demo which invokes itself with all its actually given command line parameters (so not hard coded but based on ParamCount/ParamStr)?


   
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 12 years ago
Posts: 2864
Topic starter  
Posted by: @johngaver

Wow, a pointer to an array of pointers - how fun!

It's the Apple / Objective-C way ... 😉  (insert Star Wars character here)

Posted by: @johngaver

Any chance you might be able to post a demo which invokes itself with all its actually given command line parameters (so not hard coded but based on ParamCount/ParamStr)?

I can take a look in a minute - preparing dinner right now.

Just to be clear: I'd try to handle everything in one instance of the application, since this way you'd have to ask for authorization only once, even if you have hundreds or thousands of steps.

Maybe better if I reply by email 😉 


   
johngaver reacted
ReplyQuote
Page 3 / 3
Share: