|
Volume Number: 21 (2005)
Issue Number: 5
Column Tag: Programming
Mac In The Shell
ACLs: Finer Grained Permissions (and more!)
by Edward Marczak
Zero In On Exactly What You Want.
Tiger, OS X 10.4 is here! Apple touts a grand list of new features and improvements to our favorite OS. One option that may go un-noticed if you're not looking at Server is ACLs. 'ACL' stands for Access Control List, and is an enhanced way to specify permissions on file system objects. What you won't see on Apple's pages is that ACLs work the same in both Server and client.
Introduction
Back in the December MacTech, I wrote an article introducing the Unix POSIX permission system that Apple kept in place from Darwin's BSD roots. That system has been around for decades, and modern systems are fully aware of its limitations.
Apple has been making great strides with each release of OS X. Additionally, they've realized that they don't have to strictly stick with what they've been handed in Unix-land, and can make improvements where necessary. Now that we all know, and should be comfortable with Unix (POSIX) permissions, it's time to learn ACLs: a more flexible way to assign permissions.
Slowdraw
While ACLs are new to Tiger, some of us have been using ACLs for years. Did we have some secret beta of Panther that included them? Nope. The reality is that just about every other major operating system has had some form of ACLs built in for a long time. There's a patch for most Linux distros that enable ACLs. Netware and Banyan had it over 15 years ago. And certainly, Windows has had this permissions model from the NT days. Windows? Yep. While there are security problems with Windows, I haven't ever heard of ACLs having or causing a problem (unless, of course, the administrator mis-applied them). In fact, in Tiger, Apple has chosen the Windows ACL model. Make your head spin? Naahhh. OS X is a new world, and recognizes that it also lives in one, where it needs to reach out in as many ways as possible.
In this article, 'ACL' refers to file system ACLs. Tiger also introduces Service ACLs, or, SACL. This allows you to limit services (like AFP or ftp) to particular groups and users. SACLs will be touched on next month.
Enough chatter, on to the good stuff.
GotXAttr?
For some background, it's helpful to understand metadata. Metadata, for the uninitiated, is all of the information about a file that's not actually part of the file data itself (depending on implementation, it may or may not be a physical part of the file. MP3 files store metadata as part of the file itself, for instance.). We saw this back in OS 9 in the form of type and creator codes. Last update time is also something that the file system tracks, but doesn't effect, and can change independently of, the data in the file.
Here's what's so revolutionary (ok, evolutionary - but revolutionary sounded better) about what's happening under the hood: OS X 10.4 has room for even more metadata. This comes in the form of arbitrary data, and file system attributes, like ACLs. Much like ACLs themselves, other operating systems have enjoyed the increased abilities of arbitrary file system metadata for much longer now. Anyone who ever had a chance to play with BeOS will remember what I mean - one could almost treat the file system as a large database, assign keywords to your files, and simply search for those keys later on. Similar to what Spotlight is doing on the Mac. As a musician, this was revolutionary. (I was planning at the time to use Steinberg's Nuendo running on BeOS - in fact, set up an entire studio around it. Oh, well). You could add keywords to all of your music clips (and MP3 files), and almost have them organize themselves. I expect to see similarities on the Mac with audio and video files, and the Finder's 'smart folders'.
OS X pulls this off by taking advantage of HFS+. See? The ability to write metadata was there all along! Well, OK, only somewhat recently in HFS+'s evolution did it gain this capability. Before Tiger was a reality, certainly. Much like the resource fork is an alternate data stream, the extended attributes simply get stored in a stream intended for 'inline data'.
Currently, the ability to tap into this functionality only exists at the BSD layer. No interface for CF, Cocoa, or my beloved REALBasic exists (yet) to call these functions. (Someone wanna get cracking on a RB plug-in for this?). OS X Tiger currently has several functions at the BSD layer that take care of this: setxattr, fsetxattr, getxattr, fgetxattr, removexattr, fremovexattr, listxattr and flistxattr. The 'f' variants take a file descriptor, as opposed to a path to determine which file it's working on. Each has a useful man page - check them out.
NavChooseYourPath
I must really like you, because I'm going to admit something, a vulnerability, if you will. It's something I don't like to own up to, and at times, it simply makes me check my geek cred at the door. Here goes: My C coding skills stink. I used to be better. I even used to get paid to write C code. But even then, I pretty much realized I wasn't really great. Consequently, I was much more interested in networking and databases (somewhat in graphics, too, but guess what? I'm terrible at graphics programming, also!). With all of that said, I'm going to include some crummy C code here to illustrate my points about metadata. Please don't write telling me how bad I am: I know. (However, I'm always up for improvement and a constructive dialog - don't let me down).
We're going to do this a little backwards, and show some code that reads one extended attribute from a file. That code is this:
#include <CoreFoundation/CoreFoundation.h> #include <sys/xattr.h> int main (int argc, const char * argv[]) { size_t size = 0x00; int options = 0x00; char *theBuffer = 0x00; char *theName = 0x00; char *theData = 0x00; // Figure out if there are any extended attributes. size = listxattr("myfile.txt", theBuffer, size, options); if(size > 0x00) { // There are extended attributes, let's go fetch! theBuffer = calloc(1,size); // Get a name of an attribute size = listxattr("myfile.txt", (void *)theBuffer, size, options); theName = malloc(strlen(theBuffer)); strcpy(theName, theBuffer); ssize_t vSize = 0x00; // How much data are we expecting? vSize = getxattr("myfile.txt",theName,NULL,vSize,0x00,0x00); theData = malloc(vSize); // Associate the name to the value. vSize = getxattr("myfile.txt",theName,theData,vSize,0x00,0x00); printf("%s = %s\n",theName,theData); } else { printf("This file has no extended attributes.\n"); } return 0; }
I used XCode to build this, and called it "rx" (Read eXtended). Drop to the command line, go into your build directory and create "myfile.txt":
echo This is a test > myfile.txt
Now, run rx:
$ ./rx
This file has no extended attributes.
The code to set an attribute is much more simple:
#include <CoreFoundation/CoreFoundation.h> #include <sys/xattr.h> int main (int argc, const char * argv[]) { int sxr; int options = XATTR_NOFOLLOW; char theValue[5] = "blue\0"; size_t theSize = sizeof(theValue); printf("Going to set %s, length %d\n",theValue,theSize); sxr = setxattr("myfile.txt", "color", (void *)theValue, theSize, 0, options); return 0; }
Again, I built this in XCode, and called it sx (Set eXtended). Once you build it, grab it from your build directory, and copy it to the build directory for rx. Run it, and it will create an attribute called "color" with a value of "blue" on myfile.txt. Immediately run rx again:
$ ./rx color = blue
You'll see that this time, we pick up the attribute and value associated with this file. (and now that that's over, I promise never to shove C code at anyone again).
Now, all of this metadata is cool, but it's really still in the larval stages. Go ahead and change the program to make the value more unique, run it, and then try searching for the value in Spotlight. I'll spare you the work: there's no importer for this yet, so Spotlight finds squat. The good news is that all of the BSD tools understand metadata: cp copies it, rsync, with the -E switch, syncs it. Nice. If you copy a file with metadata to a volume that doesn't support it (*cough* FAT32 *cough*), you'll get files prefixed with a dot-underscore, like it would do for a resource fork.
You have several options to protect these files: zip 'em, or create a disk image to protect the contents. Remember: this is all working on a standard HFS+ volume, and disk images work just fine in that capacity.
Again, this is something Windows has supported for a while in NTFS. Over there, they're called streams. Frankly, I've never seen them used in a practical way for straight data files - and once with malicious intent (read: virus scanners don't scan the alternate streams of a file...). However, Services For Mac does store the resource fork in a stream. That's useful. I'm incredibly interested in seeing how this gets used in the OS X world, for good or ill. Although, I don't think this will really explode until the higher-level APIs come into existence.
WaitNextEvent
Right about now, you should be saying, "I'm reading this article because I thought it would talk about ACLs!" OK, no time like the present!
If you remember from my December article, or anyplace that you've read this, OS X supports the traditional POSIX file permissions. As of Tiger, OS X now also supports file system ACLs. ACLs are a way to assign permissions to file system objects in a much more flexible fashion than before - one might even say an arbitrary fashion. Of course, I'm not saying that you don't have to plan out a permissions strategy, but consider this scenario: You have a sharepoint called 'Accounting' that stores accounting data (not too much of a stretch, right?). Within this folder are sub-folders for certain sub-groups within the department. However, certain files in each groups' folder need to be accessible by other groups, but of course, not 'everybody'. To accommodate, you set up folders for each group, with the correct group owners and permissions (770), and several 'shared' folders for files that cross between groups. Then, you are told that an outside auditor needs an account and access only to certain files that reside in each of these directories. Using just the POSIX permissions, you could do it, but you're going to jump through a lot of hoops to accomplish this, and perhaps not really structure the hierarchy in a fashion you'd like.
Apple wants you to think of ACLs as a technology for OS X Server, but like most things that server does, this can be accomplished on standard OS X as well. All Server gets you are some good-looking buttons and checkboxes. We'll discuss both OS X and OS X Server.
The ability to use ACLs must be enabled on a given volume. Using Workgroup Manager, you'll find the following new checkbox (Figure 1):
Figure 1 - Workgroup Manager's new option
Check it, and click on save. Again, good-looking buttons - but we can enable this from the command line on both client and server by using fsaclctl. For example, this will enable ACLs on the root volume:
fsaclctl -e -p /
Use sudo with this command if you're not already root somehow. Unfortunately, Apple left out the man page for this command on OS X (it's there on Server, though). Apple doesn't even have it on-line (at the time of this writing). You can get usage simply by typing 'fsaclctl' with no parameters, though. The "-e" switch tells fsaclctl to enable access control lists, and the "-p" switch tells it the mount point to perform that action on. With just the "-p" switch and no action, we can find out the status of ACL support on a mount point:
# fsaclctl -p / Access control lists are not supported or currently disabled on /.
So, let's enable them:
# fsaclctl -e -p / # fsaclctl -p / Access control lists are supported on /.
Now, how do we use them? Once again, Workgroup Manager (WGM) will let you assign and alter permissions through a GUI, and both OS X and Server will let you perform these assignments though the command line. Each time you make an assignment to a filesystem object, it becomes an entry in the access control list, also known as an Access Control Entry, or, ACE. In WGM, check the 'Access' tab for a share, and again you'll see something new (Figure 2):
Figure 2 - Workgroup Manager's Display of ACEs for a share
You can then pop open the users and groups list, drag and drop them into the list and assign permissions. Easy. What kind of permissions can be assigned? Again, WGM makes this relatively easy. Selecting 'custom' from the Permission column of an ACE shows you the full list (Figure 3):
Figure 3 - Permissions that can be assigned to an individual ACE.
How do you know that a file or folder has an ACL associated with it? Of course, you can use WGM and look at the list. But WGM only helps us with share points. In the example I gave earlier, where you may have a share point, and several folders below it that need different permissions, you need to go for the good stuff: the command line.
GetKeys
The CLI is where it's at. The GUI tools are nice, but the command line will work locally or through ssh, on Server and OS X standard. I'll swing through this quickly, and then come back with more detail.
Apple has updated the familiar utilities to deal with metadata, including ACLs. First and foremost, chmod is now responsible for most things ACL: adding ACEs, removing ACEs, ordering/changing ACLs and changing inheritance. The "+a" switch tells chmod to add an ACE:
$ chmod +a "marczak allow read" somefile.txt
Remember: even if you are 'deny'ing, you're still adding a rule to the list. Ls, with it's new "-e" switch (must be combined with "-l"), will show ACLs:
$ ls -le somefile.txt -rw-r--r-- + 1 marczak marczak 6236 May 10 22:52 somefile.txt 0: user:marczak allow read
Pretty straight forward. The "-a" switch will remove a permission:
$ chmod -a "marczak allow read" somefile.txt $ ls -le somefile.txt -rw-r--r-- + 1 marczak marczak 6236 May 10 22:52 somefile.txt
When assigned via chmod, the language you use to specify the permission is a little different than the phrasing used in WGM. Here's a list, right from the chmod man page:
The following permissions are applicable to all filesystem objects:
delete Delete the item. Deletion may be granted by either this permission on an object or the delete_child right on the containing directory.
readattr Read an objects basic attributes. This is implicitly granted if the object can be looked up and not explicitly denied.
writeattr Write an object's basic attributes.
readextattr Read extended attributes.
writeextattr Write extended attributes.
readsecurity Read an object's extended security information (ACL).
writesecurity Write an object's security information (ownership, mode, ACL).
chown Change an object's ownership.
The following permissions are applicable to directories:
list List entries.
search Look up files by name.
add_file Add a file.
add_subdirectory Add a subdirectory.
delete_child Delete a contained object. See the file delete permission above.
The following permissions are applicable to non-directory filesystem objects:
read Open for reading.
write Open for writing.
append Open for writing, but in a fashion that only allows writes into areas of the file not previously written.
execute Execute the file as a script or program.
ACL inheritance is controlled with the following permissions words, which may only be applied to directories:
file_inherit Inherit to files.
directory_inherit Inherit to directories.
limit_inherit This flag is only relevant to entries inherited by subdirectories; it causes the directory_inherit flag to be cleared in the entry that is inherited, preventing further nested subdirectories from also inheriting the entry.
only_inherit The entry is inherited by created items but not considered when processing the ACL.
Let's look at all this a little more closely. Now, even if you do not use the "-e" switch, you should notice something about "ls":
$ ls -l total 3529032 drwxr-xr-x 3 marczak marczak 102 Apr 29 15:58 Apps drwxrwxrwx + 19 marczak marczak 646 May 5 23:43 Besides
Even just using the "-l" switch will alert you if there is an ACL associated with a something in the list - notice the "+" immediately following the POSIX permission list. To see the entries themselves, use the "-e" switch:
$ ls -le total 3529032 drwxr-xr-x 3 marczak marczak 102 Apr 29 15:58 Apps drwxrwxrwx + 19 marczak marczak 646 May 5 23:43 Besides 0: user:supervisor deny list, delete 1: group:staff allow list 2: user:marczak allow list
There is one other way to see the ACL. In the Finder, Apple has updated the Get Info box. If a file or directory has an ACL, you'll get ACL info in the "Ownership & Permissions" section (Figure 4):
Figure 4 - The Finder now groks ACLs too.
This is nice, but somewhat useless. I'll explain why in a moment.
Here are some critical things to note: a) if an ACL is present, it is consulted first! If someone is trying to access a file, and an ACE matches, the "can I or can't I" search stops, and the POSIX permissions don't even come into play. If there is no matching ACL, you 'fall through' to the POSIX permissions. b) in the previous command line listing, notice that each ACE is preceded by a number - this is the most important thing to pick up on. ACLs are like firewall rules. They are read in order, and the first match determines the permissions. This is why the Finder view is useless. Compare the command line listing to the Finder listing. Not the same order, are they? (Would that have been so difficult?)
In our example above, even if 'supervisor' is a member of the "staff" group, they are denied the 'list' ability for that folder (though, you'd never know it from the Finder). Like firewall rules, this lets us set up some complex rules governing precisely which users have access to what. In a large system, this will need to be planned out, as it could become messy rather quickly.
Of course, the updated chmod command includes ways for us to keep the order of list entries correct. Use the new "+a#" switch to insert an ACE in a specific location. Given the former list of entries, we can deny a user who may be a part of the staff group by inserting the rule ahead of entry 1:
# chmod +a# 1 "emily deny read" Besides # ls -lde Besides drwxrwxrwx + 19 marczak marczak 646 May 5 23:43 Besides 0: user:supervisor deny list,delete 1: user:emily deny list 2: group:staff allow list 3: user:marczak allow list
Similarly, you can remove specific entries using the "-a#" switch:
# chmod -a# 1 Besides
will remove the entry for "emily deny list". You can also rewrite entries in place with "=a#":
# chmod =a# 1 "staff deny list"
will change our staff allow entry to a staff deny entry. Lastly, know that entry adding is cumulative if entries match. So, this:
# chmod "mikeb allow read" file.txt # chmod "mikeb allow write" file.txt # chmod "mikeb allow delete" file.txt
is equivalent to:
# chmod "mikeb allow read,write,delete" file.txt
Remember the order! If, in your testing, your ACL is not working the way you expect, run an "ls -le" on the file or directory and check the order of the entries.
Another reason you should plan/rejoice: Now, groups can be members of other groups. Fantastic! This is obviously a huge boon to anyone serving multiple groups. It's something you simply couldn't do with the basic Unix groups and POSIX permissions. Now the (small) downside: this ability is Server-only. Of course, you really only need this ability if you're managing gobs of users and groups - and if you are really managing that many users and groups, you should be using OS X Server.
Cool Stuff
The great thing about all of this metadata - the purely arbitrary, and the ACLs - is that they transition nicely into environments that don't support them. Let's say you have a Firewire drive, enable ACLs, and also assign some arbitrary metadata to some files on it. You can then take that drive and use it on a Panther machine with no harm. If permissions aren't ignored, the POSIX permissions take over. Bring it back to Tiger, and everything is where you left it. Of course, changes and additions to data won't be in Spotlight, and will need to be manually imported.
So then, how does the OS do it? Where are these permissions that can go from system to system without getting crushed, and then back to the original unharmed? This goes back to the beginning of the article. It's the whole reason I discussed metadata at all. ACLs are just another form of metadata. This particular metadata gets stored in the extended attributes of HFS+. OS X does treat the ACL metadata a little differently, of course. It's protected from you altering it outside of the 'proper' methods (chmod, the Server GUI, or one day, when we have APIs higher up the chain...). Naturally, if the POSIX permissions allow you, and you bring the file to a system that doesn't understand ACLs, you'll be able to alter the file.
Lastly, ACLs are respected through a connection to the server. So, if you're running Tiger server, but your clients are not up to that yet, feel free to start applying ACLs on the server. Because the server is responsible for the presentation and access to the files in this case, the ACL permissions still come into play.
Caveats
There are a few things to be aware of with ACLs. Unlike other metadata, cp and rsync won't copy ACLs. mv works on the same partition simply because you're not really changing the inode.
Again, in a large system, this will take some planning - including making sure that if there's no ACL match, the POSIX permissions protect you. That, and other file metadata can still be set programmatically with setattrlist(), chmod, chown and utimes, by the way.
ShutDwnPower
This was a good amount to cover this month, but wow, it feels great to be talking about the technologies in Tiger! For everyone who has had to do the POSIX dance to allow various groups to access a common data store securely, here are better tools. For everyone that can imagine all of the way to put metadata to use, more power to you! I can't wait to see the things that will come of it.
If you're interested in exploiting this power, Apple has done a good job with the respective man pages, and that's where you should start. From a system manager's perspective, the man page for chmod is an excellent introduction and reference document. Check it out.
Next month, I'll talk a little more about Service ACLs, metadata and Spotlight, and touch on WWDC 2005. Meanwhile, I'll be upgrading Servers to Tiger and planning their ACL layout...naturally!
Ed Marczak, owns and operates Radiotope, a technology consulting company that offers
application and web development, workflow management, networking and OS X rollout and planning, of course!
Upgrade at http://www.radiotope.com
Warning: include(/home/cust10011/www/site001/includes-mactech/includefiles/mt_footer.inc) [function.include]: failed to open stream: No such file or directory in /home/cust10011/www/site001_files/staticcontent/articles/mactech/Vol.21/21.05/ACLs/index.html on line 755
Warning: include() [function.include]: Failed opening '/home/cust10011/www/site001/includes-mactech/includefiles/mt_footer.inc' for inclusion (include_path='.:/usr/share/php:/usr/share/pear') in /home/cust10011/www/site001_files/staticcontent/articles/mactech/Vol.21/21.05/ACLs/index.html on line 755