Alastair’s Place

Software development, Cocoa, Objective-C, life. Stuff like that.

File Ownership and ACLs

So, you ask, these ACLs… how can I read them from my own code? Well, you might think that the acl_get() family of functions were a good choice, and indeed, they do let you get at the ACL. Unfortunately, what isn’t mentioned is that, in addition to the usual UNIX uid/gid, files with ACLs may well have UUID-based owner and group information. There’s no acl family function to get that, but you’re going to need it if you want to understand what the ACL actually means.

If you look at Apple’s code (for instance, copyfile), you’ll see all kinds of undocumented functions, including such tasty looking morsels as statx_np() and chmodx_np(). Sadly they are not documented, and rely on further undocumented functions relating to the type filesec_t. These functions are visible (in <sys/stat.h> and <sys/fcntl.h>), but they are not documented.

At first sight it doesn’t seem that there is a documented way to access this data. But there is. getattrlist() allows you to retrieve ATTR_CMN_EXTENDED_SECURITY, ATTR_CMN_UUID and ATTR_CMN_GRPUUID (and setattrlist() lets you set them as well). Sadly it isn’t at all obvious how you go about getting from the thing getattrlist() returns to something you can manipulate as an ACL with documented APIs. The getattrlist() documentation unhelpfully notes that you get

A variable-length object (thus an attrreference structure) containing a kauth_filesec structure, of which only the ACL entry is used.

Well, you can see that structure in <sys/kauth.h>, but since it’s a kernel data structure, poking around inside it is naughty and you’ll probably get burned when Apple changes it.

How, then, can we get to an acl_t from this thing, without knowing what it is? Well, it turns out that the functions acl_copy_int() and acl_copy_int_native() expect their data in just this format. This is not documented — and it probably should be, given that Apple states in the kernel sources (in vfs_attrlist.c) that

/*
 * We have a kauth_acl_t but we will be returning a kauth_filesec_t.
 *
 * XXX This needs to change at some point; since the blob is opaque in
 * user-space this is OK.
 */

The other thing that it would be helpful to know is that the ACL data returned by ATTR_CMN_EXTENDED_SECURITY is in native endianness. So the function you need is acl_copy_int_native(), and not acl_copy_int(). Again, not documented.

So, to obtain this extended security information, we need to do

struct attrlist attrList;
struct {
  uint32_t        length;
  attribute_set_t returned_attrs;
  attrreference_t extended_security;
  guid_t          owner;
  guid_t          group;
  char            buffer[4096];
} attrs;
int ret;

memset (&attrList, 0, sizeof (attrList));
attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
attrList.commonattr = (ATTR_CMN_RETURNED_ATTRS
                       | ATTR_CMN_EXTENDED_SECURITY
                       | ATTR_CMN_UUID
                       | ATTR_CMN_GRPUUID);

ret = getattrlist (path, &attrList, &attrs, sizeof (attrs), 0);

if (ret < 0) {
  // Handle errors
  ...
}

if (attrs.length > sizeof (attrs)) {
  // Need to use a bigger buffer in this case
  ...
}

acl_t acl = NULL;

if (attrs.returned_attrs.commonattr & ATTR_CMN_EXTENDED_SECURITY) {
  void *data = ((uint8_t *)&attrs.extended_security
                + attrs.extended_security.attr_dataoffset);
  acl = acl_copy_int_native (data);
}

at which point we have the ACL in acl, the owner’s UUID in attrs.owner (if attrs.returned_attrs.commonattr has ATTR_CMN_UUID set), and the group’s UUID in attrs.group (again, if ATTR_CMN_GRPUUID is set).

Likewise, if you want to set an ACL using setattrlist(), you’re somehow expected to know that you can create the blob of data you need with the acl_copy_ext_native() API.

Note that all of these functions and constants are documented, therefore in theory safe to use, though how you are supposed to use them together is not, and because Apple is using undocumented functions there is no good example showing how to use only documented APIs to find this information. Worse, the only example code I could find outside of the OS X sources, namely acl_api_fragment.c, makes use of the undocumented filesec and openx_np functions and is, therefore, a prime example of what not to do.