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 akauth_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.