User 99, Unknown

Wednesday, March 14, 2007 at 10:30 AM

Posted by Greg Miller, Mac Team Software Engineer

If you're familiar with Unix or you've poked around user accounts and file permissions in Mac OS X, you have probably seen typical user accounts such as root, nobody, and daemon. Mac OS X has an additional interesting account for a user named unknown. Unknown has the UID (user ID) number 99, which is treated specially in the kernel and in some user-level libraries. The special properties afforded to unknown are needed to make device sharing between computers as painless as possible. Let's look at what makes unknown so special.

User unknown, or more precisely, the user with a UID of 99, is treated specially in the following ways:


  1. A file owned by UID 99 appears to be owned by whoever is viewing it (but see the caveat below).
  2. Volumes mounted with the MNT_IGNORE_OWNERSHIP flag treat all files as if they were owned by UID 99.

An important caveat to the first item above is that this special treatment does not apply to the root user. If root views a file owned by unknown, the file appears as it actually is — owned by user 99. Let's look at an example, using Terminal:
$ touch file.txt
$ ls -l file.txt
-rw-r--r-- 1 jgm jgm 0 Mar 9 22:07 file.txt

$ sudo chown 99:99 file.txt
$ ls -l file.txt
-rw-r--r-- 1 jgm jgm 0 Mar 9 22:07 file.txt

Here I created the file file.txt, then changed its owner and group to 99, but the file listing still shows me as the owner. What happens when I list the file as the root user?

$ sudo ls -l file.txt
-rw-r--r-- 1 unknown unknown 0 Mar 9 22:07 file.txt
When I use sudo to list the file as root, we can see that the real owner of the file is indeed unknown. Further, we can verify the behavior when we list the file as another, non-root user:
$ sudo -u test ls -l file.txt
-rw-r--r-- 1 test test 0 Mar 9 22:07 file.txt

This special treatment is handled in the VFS layer of the kernel, specifically, in the file xnu/bsd/vfs/kpi_vfs.c. In that file, the vnode_getattr() function has logic that looks like this:


int
vnode_getattr(...) {
...

if ((nuid == 99) && !vfs_context_issuser(ctx))
nuid = kauth_cred_getuid(vfs_context_ucred(ctx));
...
}

This shows the logic used when retrieving the attributes of a vnode. (A vnode is basically an in-kernel structure that represents a file.) In the code, if the vnode is owned by UID 99, and the current calling process is not root, then it returns the calling process's UID as the file's owner. The equivalent logic for handling a GID of 99 is not shown here. This is exactly the behavior that we saw above.

The second item I mentioned above is that volumes mounted with the MNT_IGNORE_OWNERSHIP flag cause all files to appear as if they are owned by user unknown. Additionally, new files on those volumes will be created with the unknown owner and group. In many cases, the MNT_IGNORE_OWNERSHIP flag can be controlled on a per-volume basis by checking the "Ignore ownership on this volume" checkbox in the volume's "Get Info" Finder window. However, the flag can also be set by specifying MNT_IGNORE_OWNERSHIP when calling mount(2).

We can use the following C program to determine whether a volume (in this case, we'll try my iPod shuffle) has this flag set:


$ cat mnt_ownership.c
#include <stdio.h>
#include <sys/param.h>
#include <sys/mount.h>

int
main(int argc, char **argv) {
struct statfs sb;
int ignore_flag;

/* argv[1] is path to the volume or a file/folder within */
statfs(argv[1], &sb);
ignore_flag = (sb.f_flags & MNT_IGNORE_OWNERSHIP);
printf("ownership %s\n", ignore_flag ? "ignored" : "enabled");
return 0;
}

$ gcc -o mnt_ownership mnt_ownership.c -Wall
$ ./mnt_ownership /Volumes/TINY
ownership ignored

We can see here that the mounted volume for my iPod shuffle is ignoring ownership. This means that all files on the iPod will appear to be owned by me (or whomever is the current user, according to the rules discussed above), and files created on the iPod will be owned by user 99. Let's prove that with an example in Terminal:

$ cd /Volumes/TINY
$ ls -l
total 16
drwxrwxrwx 1 jgm jgm 8192 Jan 27 14:12 iPod_Control/

$ sudo ls -l
total 16
drwxrwxrwx 1 unknown unknown 8192 Jan 27 14:12 iPod_Control

$ touch file.txt
$ ls -l file.txt
-rwxrwxrwx 1 jgm jgm 0 Mar 10 16:27 file.txt

$ sudo ls -l file.txt
-rwxrwxrwx 1 unknown unknown 0 Mar 10 16:27 file.txt

From this example, we see that files on the iPod are shown as belonging to me, although root sees that they really belong to unknown. When I create a new file, it follows the same behavior.

This special behavior is also handled in the VFS layer of the kernel, about 5 lines above the vnode_getattr() snippet discussed above. The relevant code from the function is highlighted here:


int
vnode_getattr(...) {
...

/*
* Handle uid/gid == 99 and MNT_IGNORE_OWNERSHIP here.
*/
...
if (vp->v_mount->mnt_flag & MNT_IGNORE_OWNERSHIP) {
nuid = vp->v_mount->mnt_fsowner;
if (nuid == KAUTH_UID_NONE)
nuid = 99;
...
}


if ((nuid == 99) && !vfs_context_issuser(ctx))
nuid = kauth_cred_getuid(vfs_context_ucred(ctx));
...
}

We see that if the MNT_IGNORE_OWNERSHIP flag is specified, the mnt_fsowner value of the mounted file system is consulted. If that value is KAUTH_UID_NONE, then the kernel hardcodes a value of 99 — user unknown. Following that, we go through the same logic as before for handling files owned by 99.

One question this brings up: what if the mnt_fsowner value is not KAUTH_UID_NONE? In that case, the files on the volume will appear to be owned by the user specified in mnt_fsowner. In the kernel, HFS+ is the only file system that actually makes use of this feature. This fact is noted in several places with comments like /* XXX 3762912 hack to support HFS filesystem 'owner' */.

One final question: should you ever uncheck "Ignore ownership on this volume"?

Maybe. If the device is shared among several computers, like an iPod or a thumb drive, then you probably want to leave that box checked. However, if you have a large external drive that you always leave attached to one machine, then unchecking that box is probably a good idea.

(This post is adapted from a post on Greg Miller's Unixjunkie Blog.)