Beyond the Shell
sudo
allows you to gain super user privileges from the interactive shell. launchd
, installer Packages, management systems and other tools will run scripts as root
when required. This covers many cases where administrators need to influence the system on macOS.
However, for most users, macOS is exclusively the graphical interface. Users can authorize to perform certain tasks when they are administrators, like unlocking a Preference Pane or running an installer package.
These tasks are not controlled by sudo
but by a separate mechanism. The data for that mechanism is stored in the authorization
database.
- Part 1: Demystifying
root
- Part 2: The
sudo
Command - Part 3:
sudo
and Scripting - Part 4: The Authorization Database (this post)
Here there be Dragons!
As mentioned in earlier parts of this series, many pieces of macOS assume users have administrator accounts. This might not be a possible or useful configuration in your environment. But you still need to provide access to some privileges without giving a user full administrator accounts and access.
However, if you find yourself frequently editing the authorization database, you should re-evaluate your approach. Future macOS updates might change privileges or rules and then your heaviliy modified setup will need to be adpated. Small modifications or granting users admin privileges will be the the least fragile configuration going forward with future macOS releases.
What is it?
The active authorization database lives in sqlite3 database files in /var/db/auth.db
.
You can use the sqlite3
command or another tool to read the database directly.
$ sudo sqlite3 /var/db/auth.db .dump
You can use this database access to change the settings directly. However, this is not recommended. The only way sanctioned by Apple to access and change the authorization database is through the security authorization
command.
This layer of abstraction allows Apple to change the underlying data store, while keeping tools and frameworks that access the data same.
The authorization database is intialized from the property list file /System/Library/Security/authorization.plist
. In older versions of macOS administrators could replace this file with a modified version and delete the database files to have the modified property list initialize the database with the new settings. However, in current versions of macOS this file is protected by SIP, so this strategy is no longer useful.
However, the authorization.plist
file is still useful to look at the default values and get an idea of how the authorization database is configured and works. Since there is quite a lot of data in this file, it is best to open it in a graphical property list editor such as Xcode or PlistEdit Pro.
The authorization property list consists of two main dictionaries: rights
and rules
. There are also comment
fields distributed all through the file, which provide some context to what the individual elements are for.
rights
designate a certain context that permits a certain action, group of actions or access to configure some part of the os. The names of the rights follow a hierarchy and are denoted in reverse DNS notation.
This website has an overview of all the rights and rules from the authorization.plist. More importantly it shows which rights and rules are available in which version of macOS.
How it works
Within the dictionary for a given right you can find the requirements that a user needs to fulfill to gain the right. There are two main class
es: user
and rule
.
(There is also a third possible value for the class
: evaluate-mechanisms
will test multiple mechanisms in order. This is used for more complex processes such as the login window.)
The user
class will verify whether the user asking to gain the right is in a particular user group. Usually this is the admin
group. for example you can inspect the configuration of the system.preferences.datetime
right with:
$ security authorizationdb read system.preferences.datetime
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>allow-root</key>
<true/>
<key>authenticate-user</key>
<true/>
<key>class</key>
<string>user</string>
<key>comment</key>
<string>Checked by the Admin framework when making changes to the Date & Time preference pane.</string>
<key>created</key>
<real>522083506.22018802</real>
<key>group</key>
<string>admin</string>
<key>modified</key>
<real>522083506.22018802</real>
<key>session-owner</key>
<false/>
<key>shared</key>
<false/>
<key>timeout</key>
<integer>2147483647</integer>
<key>tries</key>
<integer>10000</integer>
<key>version</key>
<integer>1</integer>
</dict>
</plist>
YES (0)
(The YES (0)
at the end of the command’s output means that retrieving the data was successful.)
You can also look for the system.preferences.datetime
entry in the authorization.plist
. This tells us that when a user clicks on the lock in the ‘Date & Time’ preference pane the system will ask for authentication (authenticate-user
is true
) to check if the user a member of the admin
group.
Read the com.apple.configurationprofiles.userprofile.trustcert
right to see an example for a rule
based right:
$ security authorizationdb read com.apple.configurationprofiles.userprofile.trustcert
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>class</key>
<string>rule</string>
<key>comment</key>
<string>Install user configuration profile with certificate requiring trust change.</string>
<key>created</key>
<real>529239529.937994</real>
<key>modified</key>
<real>529239529.937994</real>
<key>rule</key>
<array>
<string>authenticate-session-owner-or-admin</string>
</array>
<key>version</key>
<integer>0</integer>
</dict>
</plist>
YES (0)
Rather than defining the approving criteria in the right itself, this right references the authenticate-session-owner-or-admin
rule. This name is already quite self-explanatory. However, we can also read the definition of the rule:
$ security authorizationdb read authenticate-session-owner-or-admin
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>allow-root</key>
<false/>
<key>authenticate-user</key>
<true/>
<key>class</key>
<string>user</string>
<key>comment</key>
<string>Authenticate either as the owner or as an administrator.</string>
<key>created</key>
<real>522083506.22018802</real>
<key>group</key>
<string>admin</string>
<key>modified</key>
<real>522083506.22018802</real>
<key>session-owner</key>
<true/>
<key>shared</key>
<false/>
<key>timeout</key>
<integer>2147483647</integer>
<key>tries</key>
<integer>10000</integer>
<key>version</key>
<integer>0</integer>
</dict>
</plist>
YES (0)
Requests to grant a right are logged. When you are unsure what the name of right is you can perform/unlock the right or task in the interface and then read the log with:
$ sudo log show --style syslog --predicate 'subsystem == "com.apple.Authorization" && eventMessage CONTAINS[c] "validating credential"' --last 1h
This will list the rights (and rules) that were requested in the last hour.
(Thanks to Erik Berglund on the MacAdmins Slack for showing me how to do this.)
Changing Behavior
You can use the security authorizationdb
command to change rights and rules in the authorization database.
WARNING: you can really mess up your system and make it unusable with the wrong settings in the authorization database. You should not test these commands on your (or anybody else’s) work machine. You should do your experimentation and testing on a virtual machine, where you can quickly revert the system to a known working state. You should only use these commands on production Macs when they have been thoroughly tested.
You need super user privileges to change the authorization db. I will use sudo
for the interactive examples.
The easiest way to change behavior is to change the right to a different preset rule. The most permissive rule is allow
which allow any user the right and not even prompt for authorization.
When you change the ‘Date & Time’ right with:
$ sudo security authorizationdb write system.preferences.datetime allow
Password:
YES (0)
And then open the ‘Date & Time’ preference pane with any user. You will see the pane is unlocked by default.
This may be just a bit too permissive. A more useful rule to use is the authenticate-session-owner-or-admin
rule, which will prompt for authentication, and accept the currently logged in user (session-owner) or any admin. This provides for some security so that other people cannot walk up and change the current user’s settings.
$ sudo security authorizationdb write system.preferences.datetime authenticate-session-owner-or-admin
YES (0)
This will allow the any user to unlock the preference pane in their own session. Note that the dialog to unlock does not prefill the user’s name and the text in the dialog still asks for an ‘administrator’s name and password’. So this may be confusing for users. Proper documentation for the user’s affected will help.
Another use case for this rule is to allow administrator account to unlock a locked screen in another user’s session (particularly useful in lab or classroom settings).
$ sudo security authorizationdb write system.login.screensaver authenticate-session-owner-or-admin
Password:
YES (0)
Now, any admin user can unlock any other user’s locked screen. This will of course drop them in that user’s login session so the admin’s have to be responsible with this privilege.
When you take a look through the rules, you will notice that some start with authenticate-
and other with is-
. The difference is that the authenticate-
rules will prompt for user name and password, while the is-
rules will grant the privilege when the user satisfies the rule with out a prompt.
There are also some rules whose names end in -nonshared
. These have the shared
key set to false
. This means that these rules will not share credentials with other requests for authentication. Shared credentials will not prompt multiple times for the same credentials in a certain time (the timeout
, usually 300 seconds/5 minutes). More sensitive settings are usually not shared.
Exporting and importing privileges
Changing the rule is a straightforward and fairly safe way to change a right’s behavior. When a rule fits your requirements, you should probably use a rule.
However, sometimes you need more fine-grained control of a right. For this you can export a right to a property list file, modify this and re-import it.
For example, say you do not want to grant every user access to a certain right but just a certain group of users even though they are not administrators. Common examples for this would be developers, teachers or lab techs. So, in our example, we create a group named techs
:
$ sudo dseditgroup -o create -n . -r "Lab Techs" techs
to be safe we will nest the admin
group in the techs
group so that members of admin
also gain all the rights of techs
:
$ sudo dseditgroup -o edit -a admin -t group techs
and add the example user beth
$ sudo dseditgroup -o edit -a beth techs
You can verify that everything worked with:
$ sudo dseditgroup -o read techs
dsAttrTypeStandard:GeneratedUID -
F9EBB33D-8A71-45EE-A65D-4DBBFF421B49
dsAttrTypeStandard:RecordName -
techs
dsAttrTypeStandard:AppleMetaNodeLocation -
/Local/Default
dsAttrTypeStandard:GroupMembers -
87EE7744-5872-47C1-9574-D247D3DD4D5C
dsAttrTypeStandard:PrimaryGroupID -
501
dsAttrTypeStandard:NestedGroups -
ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000050
dsAttrTypeStandard:RealName -
Lab Techs
dsAttrTypeStandard:GroupMembership -
beth
dsAttrTypeStandard:RecordType -
dsRecTypeStandard:Groups
Now we can assign this group to a right. Our example will be the system.preferences.energysaver
right. First export it to a property list file:
$ security authorizationdb read system.preferences.energysaver > energysaver.plist
YES (0)
Then change the value of the group
key in the property list file to techs
:
$ /usr/libexec/PlistBuddy -c "set group techs" energysaver.plist
If you want to learn more about working with property list files, I have written a book on it: ‘Property Lists, Preferences and Profiles for Apple Administrators’
And re-import the modified property list to the authorization database:
$ sudo security authorizationdb write system.preferences.energysaver < energysaver.plist
This alone is not sufficient to unlock the Energy Saver preference pane. When you look at the log with command from above, you can see that unlocking the energy saver pane requires the system.preferences
right as well. You can modify this the same way:
$ security authorizationdb read system.preferences > system.preferences.plist
YES (0)
$ /usr/libexec/PlistBuddy -c "set group techs" system.preferences.plist
$ security authorizationdb write system.preferences < system.preferences.plist
Scripting
When scripting this setup you do not have to go through the export/import cycle but can directly import a prepared property list file or here doc:
Summary
There are many levels of access privileges in macOS. Most of them are controlled by different users and groups.
The super userroot
exists on macOS like any other unix, but logging in as root
is disabled by default as a security measure. The sudo
command allows for interactive super user privileges in a shell. There are other ways administration scripts can be launched with super user privileges, such as LaunchDaemons, management systems etc.
The authorization database controls access to elevated rights in the macOS UI. You can modify it as an admin, but should do so with care.
Demystifying root on macOS: Conclusion
This ends my series on the super user in macOS. I hope it clarifies some confusing terms and configurations. As admins we have to use these privileges regularly. But, as the saying goes: “With great power comes great responsibility.”
Understanding how things work and how they affect the system will help you understand what to do when and how to avoid unexpected consequences.
- Part 1: Demystifying
root
- Part 2: The
sudo
Command - Part 3:
sudo
and Scripting - Part 4: The Authorization Database (this post)
The “sudo security authorizationdb write system.login.screensaver authenticate-session-owner-or-admin” command seems to have reverted back to default (ui-loginwindow something or other) after updating to 10.13.5.
I was able to run the command again to return to “authenticate-session-owner-or-admin” (after a reboot), but interesting that OS updates may revert those settings.
I have a question about nested groups and authorisation settings.
All of my standard users are members of the staff group. But not all standard users are equal: some are faculty and some are students. I want faculty to have some admin permissions, but I don’t want students to have anything other than what a standard user would have (and sometimes not even that, but I’ll keep it simple for now).
So I create a new group, faculty, and I add it to the staff group so that it inherits all the authorisations:-
dseditgroup -o create -n . -t group faculty
dseditgroup -o edit -a faculty -t group staff
Now I want to grant the faculty group some admin permissions. Each of these follows this example, which is familiar to everyone:-
security authorizationdb read system.preferences.energysaver > tmp.plist
PListbuddy -c “set group faculty” tmp.plist
security authorizationdb write system.preferences.energysaver < tmp.plist
The problem here is that this grants the faculty group admin privileges (the ability to unlock the padlock) on the Energy Saver control panel, but removes it from the admin group, which is definitely not the effect I want. It means the system administrator is no longer able to unlock the padlock on the Energy Saver control panel.
Is there any way to tell the security command to authorise more than one named group? In this case, both faculty and admin? Failing that, is there a reasonable way of defining this group hierarchy so that faculty get the same authorisations as staff but not those of admin except the ones I want?
Create a group ‚facultyAndAdmins‘ and nest both the faculty and the admins group in that.
Don’t know why I didn’t think of that. That’s the ticket, all right. Cheers!
The ‘umbrella’ group idea worked perfectly. Now, I’m trying to create a new authorizationdb rule, similar to is-admin or is-developer or any other is-* rule, that matches my faculty group, because I’ve discovered that some settings are still blocked because they require the is-admin rule to be satisfied (in this case, com.apple.wifi).
So I dumped the is-admin rule, changed the group to faculty, renamed it to is-faculty, and loaded it back into the database. When I added this new rule to an existing plist (i.e., added is-faculty to the rule array in the plist for com.apple.wifi) and tried to change a setting in the UI, my group member’s credentials were rejected.
I noticed, on dumping the rule back out, that, on input, the security system must have added a couple of new entries: identifiercom.apple.security and requirementidentifier “com.apple.security” and anchor apple. I assume these are causing the trouble, since other rules (such as is-admin) don’t have these extra bits.
Is there a way to force the rule into the authorizationdb without getting it modified on the way in? Is there a better way to accomplish what I’m trying to do?
Sorry to bother you again. Thanks for any thoughts.