Tuesday, August 12, 2008
The mystery of a plus
Well, today I noticed that "long" listing in one of my directories appends "+" sign to each permissions string, like that:
drwxrwxr-x+ 13 user 500 4096 2008-05-10 02:07 DivX drwxrwxr-x+ 34 user 500 4096 2008-05-10 08:44 Raw
I was used to seeing this extra plus a lot under Cygwin, and never really gave it much thought always attributing this to some Cygwin peculiarities. But this time around this wasn't Cygwin at all - this was pristine Ubuntu 8.04. Given the specific path where this pluses were present, it was apparent that this is somehow related to Samba - these directories were originally uploaded with Samba daemon. So, I did understand from the get-go that in all likelihood Samba server is assigning some special flag to these files and "ls" is trying to notify me of the existence of this flag. I only need to figure out which specific flag it is (and how to remove it). This should be easy. Or so I thought.
The first page I stumbled upon in Google search had this to say:
The character after permissions is an ACL or extended attributes indicator. This character is an @ if extended attributes are associated with the file and the -@ option is in effect. Otherwise, this character is a plus sign (+) character if a nontrivial ACL is associated with the file or a space character if not.
ACL stands for "Access Control Lists" and allows for some fine-tuning of users' access permission on the level of individual user.
Ok, so what now? Same page listed some ACL-related command line options to ls, like '-v'; none of them was recognized by my ls. Well, that was to be expected since these were options for Solaris ls. How about Linux? No problem, said Google, you can use commands getfacl/setfacl to get/set ACL for a given file.
getfacl does indeed generate some output, but it turned out it does so even in absence of any ACLs, simply interpreting default Unix behavior based on file ownership and permissions. Attempts to set and/or clean ACLs for a file failed, and for a good reason: apparently, in order for ACL to be in effect for a mounted file system, mount option 'acl' must be included, as explained in some details e.g. here.
Fine, so if this is not ACL to blame, how about extended attributes? (Actually, extended attributes is not something parallel to ACLs; ACL implementation is based on extended attributes, which merely allows to attach more or less arbitrary "extended" info to each file in a supported file system; ACL is about how its attributes are to be interpreted by file system and kernel; attributes themselves bear no such interpretation). For one thing, this seemed as unlikely, since to use extended attributes one needs another mount option, "user_xattr". In fact, I didn't even have a package "attr" installed, which provided utilities getfattr/setfattr very similar to getfacl/setfacl.
Nevertheless, I installed the package and tried running getfattr on my files. None, as expected.
At this point in time I decided that the fastest way to get to the bottom of this puzzle was to look at the source of "ls" to see what kind of test makes it decide to append this extra "plus".
This was surprisingly simple enough to do; though this is somewhat off-topic, here is a brief overview what I did to compile my own debuggable "ls", for future references:
- Install packages dbs and debhelper (for build);
- Install packages xxgdb or ddd (for debugging);
- Run command "dpkg -S `which ls`" to figure out which package "ls" belongs to. It is "coreutils";
- Retrieve the source from http://packages.ubuntu.com/hardy/coreutils;
- Untar, change dir to coreutils-6.10 and following instructions from file debian/README.build run command "debian/rules setup";
- Now change dir to build-tree/coreutils-6.10 and run " CFLAGS='-g' ./configure";
- Finally, do "make";
- Now your fresh debuggable "ls" is available as "src/ls".
Anyway, it quickly turned out "ls" is calling function getfilecon() and upon receiving some non-trivial output appends "+" markup.
So, what is getfilecon()? It is part of SELinux toolkit; SELinux is, of course, a special security-enhancement system for Linux, which is totally separate from traditional Linux access restriction tools, including ACL; it works by assigning special "roles" to files, processes and users and then keeping a (potentially) huge policy database of who-can-do-what; it was originally developed by NSA and open-sourced in 2000.
On a practical level, SELinux-enabled system (Ubuntu Hardy being one of them) have special CLI switch "-Z" for many file and process-management utilities to report/take into account SELinux "context". Indeed, this finally allowed me to solve first piece of the mystery - force "ls" to tell me what it means my "+", by using "-Z":
% ls -1Z user_u:object_r:file_t DivX user_u:object_r:file_t Raw
The only trouble is, of course, that SELinux was never enabled on my system! This could be seen with e.g. "id -Z"
% id -Z id: --context (-Z) works only on an SELinux-enabled kernel
Anyway, enough suspence, here is what (I think) is going on. Not unlike ACL, SELinux keeps its "context" in extended attributes. Whether kernel has SELinux enabled or not, utilities which are told to assign certain SELinux "types" to files, are free to do so using e.g. setfattr. These attributes, of course, will have no impact whatsoever on security of the system, but will be reported by utilities like "ls" which, again, simply looks at the specific attribute.
In fact, the name of this attribute is "security.selinux"; indeed, I can verify this with "getfattr" :
% getfattr -n security.selinux -d Raw # file: Raw security.selinux="user_u:object_r:file_t\000"
So why woudn't "getfattr -d" report this? Isn't it in the absence of "-n" supposed to return all attributes?
Well, yes and no. By default, it reports all user attributes, that is, attributes with names which begin with "user.". To truly report all attributes, add "-m -" :
% getfattr -m - -d Raw # file: Raw security.selinux="user_u:object_r:file_t\000"
You can, of course, assign this attribute with command "setfattr -n security.selinux -v <value> <file name>" or remove it altogether with "setfattr -x security.selinux <file name>".
This, finally, answers the question we asked in the beginning - how to remove this "plus" from "ls -l" output - and thus concluded our investigation.
Bonus track. Some SELinux-related references: