Friday, October 06, 2006

Finder's Locum

Jumping right in, let's consider this little example:

$ mkdir -p foo/bar
$ sudo chown -R root:wheel foo
$ rm -rf foo
rm: foo/bar: Permission denied
rm: foo: Directory not empty

We create two directories, foo/ and a subdir bar/. We change both of these directories to be owned by root and in group wheel. Then, as a non-root user, we try to recursively delete foo/, and not too surprisingly it fails.

Notice that an error is displayed for foo/bar before the error for foo/. This is because the system call to remove a directory -- rmdir(2) -- requires the directory to be empty before it can be removed. This means that directory hierarchies are removed in a depth-first order. In order for foo/ to be removed, it must be empty, so to make it empty we must remove foo/bar/, etc.

As a quick aside, removing a file (or directory) in Unix does not require write permission to the file! Let me repeat that. You can remove a file if you have write access to the directory in which the file resides -- even if root owns the file. Quick example:
$ mkdir test
$ cd test
$ touch hi.txt
$ sudo chown root:wheel hi.txt
$ ls -al
total 0
drwxr-xr-x 3 jgm jgm 102 Oct 6 23:13 ./
drwxr-xr-x 31 jgm jgm 1054 Oct 6 23:13 ../
-rw-r--r-- 1 root wheel 0 Oct 6 23:13 hi.txt
$ rm hi.txt
$ ls -al
total 0
drwxr-xr-x 2 jgm jgm 68 Oct 6 23:15 ./
drwxr-xr-x 31 jgm jgm 1054 Oct 6 23:13 ../

This shows that I can delete a root owned file as long as I have write access to the directory. (See chmod(2) for details about how the sticky bit on a directory affects this behavior [this is why shared tmp directories generally have the sticky bit set].)

Thinking back to the original issue, this also explains why we were unable to rm -rf foo. Because foo/bar/ needed to be deleted first, but in order to delete that we need write access to its parent dir. But foo/ was owned by root and we didn't have write access to it. So, all that makes sense now.

Interestingly, if we use the Finder and drag the folder foo/ to the trash, we are able to empty the trash. We're not prompted for a password, it just works. How does the Finder do this?

Well, the Finder application links with a private Apple framework named DesktopServicesPriv.framework, which helps with taking out the trash. Bundled as a resource in the framework is a setuid root binary named Locum, which the Finder uses to delete files that it normally wouldn't have access to delete.

$ cd /System/Library/PrivateFrameworks/DesktopServicesPriv.framework/
$ ls -l Resources/Locum
-rwsr-xr-x 1 root wheel 108940 Mar 26 2006 Resources/Locum*

And we can watch what happens when we drag foo/ to the trash then empty it, using fs_usage.
$ sudo fs_usage | grep Locum
23:33:48 rmdir /.vol/234881026/2148678/bar 0.000303 Locum
23:33:48 rmdir /.vol/234881026/2147406/foo 0.000264 Locum

Now the question is, is this secure and safe? Well, it's probably fine. I imagine that Apple has rigorously tested and reviewed Locum. Conceivably, it has smarts to guarantee that it only deletes things out of a .Trashes folder. It possibly even does a little handshake to guarantee that its parent is a Finder process. This is all speculation, but the point is, I don't immediately see any big holes here (though this is very different than the behavior one would find on a typical Unix system).

I think the only remaining question is, what the hell does Locum mean? Thankfully we have Wikipedia to help us out:


Anonymous said...
This comment has been removed by a blog administrator.
Nicholas said...

Thanks, this explains something I've always wondered - when I try to replace an application containing some setuid tool with another, Finder refuses to replace it saying it has no permissions to do so, but dragging the original app to the Trash and emptying it works fine.

junkiesxl said...

This has been bugging me for quite some time, thanks for putting in the time and finding out what it was all about