
This isn't just a problem of user misconfiguration. In the last three years, I've submitted vulnerability reports to four major software vendors, because their OS X installer packages altered the POSIX permission settings (and sometimes ownership) on some important files or directories. If an installer package adds or alters an ACL (to your tax records, for example), it might go unnoticed (but could make you quite unhappy when you found out).
Unfortunately, there aren't any good tools to find ACLs, or to verify that they haven't been altered. Apple's Verify Permissions feature in Disk Utility has a history of bugs, and it's quite slow. Because Disk Utility is a closed implementation, it isn't even clear that it checks ALL filesystem objects (it appears to only check what Apple wants to check). And although it reports that ACLs exist, it isn't clear that it checks all the contents of the ACL metadata.
In an effort to quantify how many ACLs are lurking on my new Leopard install, I wrote the following zsh script. I realize I could have thrown together an actual app, or a Python/Ruby/etc script, but I prefer to prototype things like this in the shell, particularly because the shell is actually quite good at things like stream editing.
My script found over 800 objects with ACLs (out of just over 1M total filesystem objects).
I've asked the guys at the Open Source Tripwire project to add ACL metadata to the Tripwire tests. In the meantime, I may disable ACL support on my system, since it's overkill for my single-user operations, and the risk far outweighs the value to me.
About the script:
There are control characters in the script, but they show below as ^A and ^B, instead of A and B. If you're going to use this script, you should replace them. If you're using vi, you do this by typing VA and VB in place of ^A and ^B. The vi commands would be:
:%s/^A/<ctrl>v<ctrl>a/g
:%s/^B/<ctrl>v<ctrl>b/g
It is also important to leave the newlines after the line that begins:
cat ~/acls/everything_$dt | tr '"
The newlines are in the middle of single quotes, and the single quotes are there to enclose the newline character as part of the command.
You'll need to create a directory, ~/acls , to house the files this script creates, and you'll need to run the script with sudo (if you want to traverse the whole filesystem). If you only want to look at a subset of your filesystem, change the find command (from find / to find ). You can also use the -maxdepth flag for the find command, to limit how deep it searches in subdirectories. The script produces several files, tagged with the date and time:
~/acls/findacl_timer_<date and time>
This file is a summary of how long it took (3 hrs on my system).
~/acls/everything_<date and time>
The initial list of all filesystem objects, in case you want to refer to it.
~/acls//all_acl_objects_<date and time>.csv
A list of all the objects with ACLs, ready to be opened in OpenOffice, delimited by ^B instead of commas. (^B and ^A were chosen because they are not likely to be in a legitimate filename; commas do appear in some OS X filenames.)
If you do open the .csv file with OpenOffice, you'll need to change the import separator to ^B. To do that, you de-select the box for Comma, select Other, and use the OS X Character Palette (Code Tables, Other Encodings, 0002, Insert) to insert ASCII code 0002.
I recognize this could be done more elegantly and more efficiently. It's a quick and dirty analysis tool. Please suggest alternatives, if you know of them; I didn't find any clean way to get this data (it would have been great if there was an ACL extension to the find command, but there isn't).
#!/bin/zsh
if [ $EUID -ne 0 ]; then
echo YOU MUST SUDO TO ROOT TO RUN THIS
else
dt=`date "+%Y%m%d_%H%M%S"`
echo Start: `date` > ~/acls/findacl_timer_$dt
# Traverse entire volume, printing ACL data (-e)
# Put list in everything_$dt
find / -exec ls -alde {} ; 2>/dev/null > ~/acls/everything_$dt
echo Find done: `date` >> ~/acls/findacl_timer_$dt
# Search list of everything as follows:
# - convert all newlines to ^A (this is a way to find <newline><space>, which marks an ACL entry)
# - convert all occurrences of ^A<space> to ^B (this removes the former newline and merges the ACL entry onto the previous line)
# - convert all ^A back to newline
# Search the result for ACL entries only (a ^B, followed by the ACL number)
cat ~/acls/everything_$dt | tr '
' '^A' | sed -e 's/^A /^B/g' | tr '^A' '
' | grep '^B[0-9*]:' | sed -e 's/ /^B/' -e 's# /#^B/#' > ~/acls/all_acl_objects_$dt.csv
echo Done: `date` >> ~/acls/findacl_timer_$dt
fi