Make user verify administrator password with timeout

No It’s not. Since Mac OS X 10.6 scripting addition commands have “Context” (see it’s info.plist). The do shell script has the context “User” which means they are bounced back into the “current application” target if they are used in an another application block. Only commands with the context “Process” are allowed to be loaded into external applications and executed by them, like display dialogs. So wrapping a tell application “Finder” around it has no affect, you will only add one silent error to your script (see event log).

Thanks for the info on the context in the info.plist, hopefully I won’t have to look much into that file to figure out what goes wrong. I realized it worked like you write further down, as “which evebt” matters more than which tell block it appears in. I really should start to the error log more; an error number -10004 did indeed show up in the event log, but if I remember correctly, this just indicates that the do shell script was called in a bad context.

(The MacError utility should really have given more details. it shows the telBadHandleErr, whereas Regulus’s script, shows the errAEPrivilegeError. The script can be found here.)

The “silent” class of errors, can also confusing, they aren’t even trapped by a try on error - end try block. (I’d really like to call them “errors that don’t create runtime errors” :slight_smile:

Excactly, that’s why it is called Context in the info.plist as well. However, it would be useful if the dictionary had some information about this as well, now we have to execute an command and try in another context when we see an error occur.

If they throw a normal error, in 10.6 the MacScripter forums would have been flooded with posts as the Apple community. Because the error is obvious, and AppleScript is an language that tries to solve mistakes by the developer their choice is nothing more than expected to me.

errors-that-will-be-fixed-for-you-at-run-time was too long compared to the word “silent” :cool:

Hello.

Wasn’t it abit like that when they introduced the “privelege elevation” with Mountain Lion? Wasn’t it like you got a “hard error” then, but that they have “cut some slack” with Mavericks?

I remember standard additions commands throwing errors inside tell application blocks way back then, except for beep, and maybe delay.

This was interesting, thanks. :slight_smile:

That was solved by the security update 005 for Leopard. 10.5 had it’s own new scripting addition structure and improved event handling and got rid of the 10.4 and earlier routines. 10.6 had again a more secured version of the 10.5 loading mechanism and event handling, but 10.6 is forgiving (at least at my desk here). Mountain Lion was all about sandboxing and how you should use NSUserScriptTasks to run scripts (shell or AppleScript) safely from an sandboxed app but hasn’t had any updates on scripting additions. I think it was 10.7 who stopped supporting the 10.4 style scripting additions for security reasons already.

Thanks. :slight_smile:

And in some cases they are actually silent, at least since Mavericks. Take this script:

tell application "Finder"
	random number
end tell

You can see the error in the log. Now try this version:

use scripting additions
tell application "Finder"
	random number
end tell

No sign of the error, but the command is still being sent to current application.

From the ASLG:

Thanks, I was aware of better optimization for scripting additions since Mavericks (from what I have read in the AppleScript release notes for 10.9) but haven’t noticed this.

Wow, you guys really took off with this one! Makes for a good read :slight_smile:

I should explain my problem a little bit more. I have ControlPlane installed to switch between “safe” and “unsafe” contexts (like being safe at home vs. unsafe at the office). This triggers a bunch of system behavior, including toggling whether a password is required to wake the machine.

The use case is that my computer is happy and safe at home, which implies that it can wake without using a password, and then it is taken away by some evil doer. The evil doer opens the computer, and ControlPlane switches to an unsafe contexts because the home displays and network adapter are no longer available. When this happens, ControlPlane runs a script which, among other things, turns on the password when waking. But the computer was already awake when “require password to wake” was set to true, so the evil doer still have access to my machine!

My goal here was to implement a dialog into which the user must enter an administrator password. If the dialog fails, then the computer locks and a password is required to awaken it. Likewise, if the dialog times out the computer locks.

I’m using the password prompt from

do shell script "whatever" with administrator privileges

because I use KnockApp (http://www.knocktounlock.com), which allows auto entry of your password when a “legitimate” dialog appears asking to enter the administrator password (like when installing an application or unlocking security preferences). If I use an alternative method like

try
	set thepass to text returned of (display dialog "password:" giving up after 15)
	do shell script "whatever" password thepass with administrator privileges
on error
	LockScreen()
end try

then KnockApp won’t be triggered by the dialog and I have to manually enter my password.

So I’m after a “built-in” administrator password dialog with a timeout. Is this something I need ASObjC for or can I find a solution within AS?

And Stefan, I have my computer set to require a password after 5 seconds, so if I use pmset displaysleepnow and then wake the computer immediately a password isn’t required, whereas the keychain menu item requires it immediately like I want.

Edit: Let’s ignore how I’m concerned about security and yet I’m using a program that lets me unlock my computer by tapping on my phone :slight_smile:

FWIW, ASObjC is unlikely to help you.

Does anyone know a way to get a “normal” administrator password dialog with a timeout? Maybe a 3rd party application?

I think it will not, at least using vanilla AppleScript StefanK solution is the closest you’ll get. I think you have to write the 3rd party software yourself in ASObjC, an osax or an scriptable Objective-C background application.

Edited

A brutal approach.

What could have been a good idea, is to get the process id of the running script, which should be possible.

You send this into a do shell script as a paramter, the do shell script runs in the background , that process writes its process id into a file. Then the shell script sleeps for 15 seconds, and then it kills the process that runs the script.

The do shell script with administrator privileges, does in addition to what it should do; kill the process, that is supposed to kill the process that runs the script. It does so, by reading the process id from the filename, and then killing that process.

It is a kluge, and a brutal one, but that way, at least the script is terminated if 15 seconds passes without user interaction.

Hopefully this may spawn som other, and possibly better ideas. :slight_smile:

something like this:

set theScript to "do shell script \"echo 'hello world'\" with administrator privileges"

set PID to do shell script "osascript <<<" & quoted form of theScript & " &>/dev/null &
echo $!"

delay 15

do shell script "kill " & PID

Something like that, or a properly set up Applet, that could be called by a script, when the info.plist, and everything is in order, then I see no problem with using system events to get the process id, and the script could still keep running, and exit in a more proper way than just being killed.

The applet approach would be to send with its process id to the a do shell script before the one with the administrator privilege, then the do shell script with administrator privileges, that is augemented with reading the id of the child process and kill it. Then the handler of the applet would just return an “OK”, to the calling script, or nothing if it was killed by the child process after 15 seconds, because the Applet is hanging on the do shell script with administrator dialogue. :slight_smile:

FYI: Kill sends SIGTERM by default which is almost equivalent to quitting an application properly. As long as you don’t send SIGKILL, you’re doing fine. But like Applications, it completely depends how well the signal handle function is implemented.

I actually envised, the launching of the applet, and calling the handler to be done inside a “try block”. I think I can manage to kill an applet by sending a signal to it, without generating a crash-report, which I guess is what SIGTERM does. :slight_smile:

I am not doing anything. :wink: I was just sketching up a probable way to solve the problem, with something that really should work, so that the “with administrator privileges” do shell script can time out.

There must be put som effort into this in order to create a working solution, with testing of every step and so on. The only thing that I guess can be taken lightly, is that there will be no race conditions, with regards to the file name the process id is written into. There is a way around the lack of timeout for the do shell script, or other commands that also are from scripting additions, as long as someone are willing to invest time in creating a practical implementation.

That is really wants it, I don’t, -at the moment at least, (I reckon it may take up to three hours to make a solution that works, with testing and everything).

Edit

Having a do shell script, spawn a background process, containing an osascript, telling the applet to quit, would probably not work, all the time that the do shell script is running, so the only option is killing it, that will be brutal, maybe the only way to get rid of any crash reports, is to set debug options for the applet.

I should mention, that I am unsure whether you can excempt an app from a crash report when it crashes, by setting properties directly for it, but you can disable crashreporter globally, before and after the app is executed: OSX Daily

That’s the limitation of an applet, try quitting it by hand it won’t work either. Putting it in an idle handler won’t work as well. The other option is not killing the applet but killing the command the applet is executing, but that makes the whole applet kind of overhead and bring us back to my example first.

Again, what would the point of an applet be then? Applications, processes that have GUI and connected to the window server/console, has to be able to listen to AppleEvents and has to support a required set of Apple events. Applications, including applets, are quit by an AppleEvent like any other applications. So to me applications should therefore never be quit otherwise than sending an quit AppleEvent, unless it hangs of course. BSD processes, like osascript, can only be quit by sending a signal to it and kill is perfect for the job. In short: When stopping an application you send an AppleEvent and when stopping a BSD process you send a signal.

I should mention, that I am unsure whether you can excempt an app from a crash report when it crashes

Normal killing (signal SIGTERM) doesn’t cause an crash report. When using signal SIGQUIT it will generate a crash report. When the process is halted it will generate a crash report on next launch depending on application (applet won’t).

Hello.

I did mix up the SIGTERM and SIGQUIT signals. The whole idea for using an applet, was to not just kill the script, but let the script be informed of whether the timeout occured or not. So I did need an applet for doing the do shell script, as I needed something to quit other than the calling script. You can kill an OSX application, which an applet is a form of, therefore I thought that would be the easiest approach to achieve the stated goal, of informing a script of a timeout. And it is also possible to disable the CrashReporter before, calling the Applet, and enable it when it is done, thereby not cluttering the screen with a Crash Report dialog, when the user finally unlocks it.

I am not saying that this is a “clean” solution, but it is a solution that can be made to work. :slight_smile:

So here’s a working solution.

Three parts: The calling script, the applet to generate the dialog, and a little 1-byte file to store the return value of the applet so that the calling script knows the result (since I don’t know how else to get at the return value)

Calling script (this goes into whatever script it is that you want to request the administrator password):

property PassEnteredFile : ((path to scripts folder from user domain) & "Background scripts:ScriptData:PasswordEntered.b") as string

property PromptPassApp : ((path to scripts folder from user domain) & "Script Resources:Scripts for Services:PromptForPassword.app:") as string
property PromptName : "PromptForPassword"

property DelayTimeOut : 15

on CheckPassword()
	local PassEntered, PassFileRef, CloseTerminal, ScriptStr, PID, StartTime
	
	set PassEntered to false
	set PassFileRef to open for access PassEnteredFile with write permission
	set eof of PassFileRef to 0
	write PassEntered to PassFileRef as boolean
	close access PassFileRef
	
	ignoring application responses
		tell application PromptPassApp to activate
	end ignoring
	
	set StartTime to current date
	repeat until ((current date) - StartTime > DelayTimeOut)
		if not appIsRunning(PromptName) then exit repeat
		delay 0.25
	end repeat
	
	if appIsRunning(PromptName) then
		set ScriptStr to "ps aux | grep -v grep |grep -i " & quoted form of PromptName & " | awk '{print $2;}'"
		set PID to do shell script ScriptStr
		do shell script "kill " & PID
	end if
	
	set PassEntered to read (PassEnteredFile as alias) as boolean
	
	return PassEntered
	
end CheckPassword

password prompt script to be saved as applet:

property PassEnteredFile : ((path to scripts folder from user domain) & "Background scripts:ScriptData:PasswordEntered.b") as string

on run
	local PassEntered, PassFileRef
	
	try
		do shell script "echo" with administrator privileges
		set PassEntered to true
	on error
		set PassEntered to false
	end try
	
	set PassFileRef to open for access PassEnteredFile with write permission
	set eof of PassFileRef to 0
	write PassEntered to PassFileRef as boolean
	close access PassFileRef
	
end run

And the ‘PasswordEntered.b’ file will be created wherever you have the path set to in the two scripts.

You can do whatever you want with the results of CheckPassword(). In my case, if it returns false (meaning that the password dialog either timed out or was cancelled) I lock the screen immediately.

It ain’t pretty but it works! And way less than 3 hours to make, so long as you don’t count the failed attempts at getting this done before I asked the question here!

I appreciate the help, as always. I had considered the need of using another script/app/process in order to accomplish this. Little disappointed it came to this, but at least it works, and now I can for the user to authenticate him/herself and have the screen lock to a password protected machine if they can’t.