I want to be able to have them stay in the GUI, get presented with a pop-up that says "Uh, you need root to do that. Please enter your password."
This is exactly what the MacOS Security API's AuthorizationExecuteWithPrivileges()
function was created for.
You can call AuthorizationExecuteWithPrivileges()
directly with python's ctypes.
For example, consider your parent script running as your normal, non-root user. If you try to just run setuid(0)
, then it will fail with
PermissionError: [Errno 1] Operation not permitted
Instead, let's create another script named root_child.py
, which we'll execute as root with AuthorizationExecuteWithPrivileges()
Child (root_child.py)
#!/usr/bin/env python3
import os
if __name__ == "__main__":
try:
os.setuid(9)
print( "I am root!" )
except Exception as e:
print( "I am not root :'(" )
Parent (spawn_root.py)
We can execute the above root_child.py
script as root from our non-root script spawn_root.py
:
import sys, ctypes, struct
import ctypes.util
from ctypes import byref
# import some C libraries for interacting via ctypes with the MacOS API
libc = ctypes.cdll.LoadLibrary(ctypes.util.find_library("c"))
# https://developer.apple.com/documentation/security
sec = ctypes.cdll.LoadLibrary(ctypes.util.find_library("Security"))
kAuthorizationFlagDefaults = 0
auth = ctypes.c_void_p()
r_auth = byref(auth)
sec.AuthorizationCreate(None,None,kAuthorizationFlagDefaults,r_auth)
exe = [sys.executable,"root_child.py"]
args = (ctypes.c_char_p * len(exe))()
for i,arg in enumerate(exe[1:]):
args[i] = arg.encode('utf8')
io = ctypes.c_void_p()
print( "running root_child.py")
err = sec.AuthorizationExecuteWithPrivileges(auth,exe[0].encode('utf8'),0,args,byref(io))
print( "err:|" +str(err)+ "|" )
print( "root_child.py executed!")
Example Execution
Note that, because the credential challenge for AuthorizationExecuteWithPrivileges()
comes via the GUI, you must execute this from within the GUI. If you attempt to execute the above scripts, for example, over a SSH in a tty, you'll get an error -60007
, which is errAuthorizationInteractionNotAllowed
and means:
The Security Server denied authorization because no user interaction is allowed.
user@host ~ % ./spawn_root.py
running root_child.py
err:|-60007|
root_child.py executed!
user@host ~ %
However, if executed from the Terminal app in the GUI, then it prompts the user for their password.

If the user successfully enters their credentials correctly, then the root_child.py
script is executed with root privileges.
user@host ~ % ./spawn_root.py
running root_child.py
err:|0|
root_child.py executed!
Additional Information
Security
Note that AuthorizationExecuteWithPrivileges()
has been deprecated by apple in-favor of an alternatve that requires you to pay them money. Unfortunately, there's some misinformation out there that AuthorizationExecuteWithPrivileges()
is a huge security hole. While it's true that using AuthorizationExecuteWithPrivileges()
incorrectly can cause security issues, it is not inherently insecure to use it.
Obviously, any time you run something as root, you need to be very careful!
AuthorizationExecuteWithPrivileges()
is deprecated, but it can be used safely. But it can also be used unsafely!
It basically boils down to: do you actually know what you're running as root? If the script you're running as root is located in a Temp dir that has world-writeable permissions (as a lot of MacOS App installers have done historically), then any malicious process could gain root access.
To execute a process as root safely:
- Make sure that the permissions on the process-to-be-launched are root:root 0400 (or writeable only by root)
- Specify the absolute path to the process-to-be-launched, and don't allow any malicious modification of that path
Further Reading
AuthorizationExecuteWithPrivileges()
Reference Documentation
- https://github.com/cloudmatrix/esky/blob/master/esky/sudo/sudo_osx.py
- https://github.com/BusKill/buskill-app/issues/14
- https://www.jamf.com/blog/detecting-insecure-application-updates-on-macos/