Cover V10, I05
Article

may2001.tar


Achieving More Flexible File Permissions Using Solaris ACLs

Ross Oliver

The fixed set of UNIX file permissions for owner, group, and world is simple to understand and easy to manage. However, the simplicity of this scheme is also its most frustrating limitation. Most UNIX systems administrators have at one time or another wished for just one more set of permissions to accomplish some necessary task.

Traditional workarounds include multiple user accounts, multiple group memberships, and set-UID programs. Although these methods work, they are not ideal because they add complexity, require root access to implement, and often raise security problems.

In the Solaris world, these limitations were finally overcome with the introduction of Access Control Lists (ACLs). Introduced in Solaris 2.5, ACLs are additional sets of read/write/execute triplets that can be added on to files, directories, devices, or any other file system objects. Using ACLs, systems administrators as well as ordinary users can now fine-tune access without resorting to multiple groups or set-UID programs. This article is a practical look at how to use ACLs on a real network.

Managing ACLs

In the fine UNIX tradition, the utilities to manage ACLs have the awkward but utilitarian names getfacl and setfacl (I pronounce them GET-fakle and SET-fakle). getfacl takes file names as command-line arguments, and outputs a textual representation of all ACLs currently in place for the named files. setfacl accepts command-line options to install, modify, or remove ACLs.

Here is an example of getfacl output for a file named "samplefile" that has no ACLs, only the standard UNIX permissions:

$ getacl samplefile
# file: samplefile
# owner: reo
# group: sysadmin
user::rw-
group::r--      #effective:r--
mask:r--
other:r--
The "user," "group," and "other" fields correspond to the standard UNIX permission bits. The "mask" field works like a real-time umask, screening off unwanted access for "group" and "other." So, if "other" were set to rwx, and "mask" set to ---, the effective permissions for "other" would be ---.

To give the user "tdk" write permission on the above file, the setfacl command line would be:

$ setfacl -m user:tdk:rw- samplefile
$ getacl samplefile
# file: samplefile
# owner: reo
# group: sysadmin
user::rw-
user:tdk:rw-    #effective:r--
group::r--      #effective:r--
mask:r--
other:r--
Note the new "user:tdk" entry, and the "#effective" field, which displays the combined results of the user permissions and the mask. In this case, #effective indicates the mask must be changed for the new ACL to have the desired effect:

$ setfacl -m mask:rw- samplefile
$ getacl samplefile
# file: samplefile
# owner: reo
# group: sysadmin
user::rw-
user:tdk:rw-    #effective:rw-
group::r--      #effective:r--
mask:rw-
other:r--
To indicate the presence of ACLs, Sun modified the format of the ls -l command so that a "+" character is displayed immediately to the right of the standard file permissions. For the example file above, ls -l would show:

$ ls -l samplefile
-rw-rw-r--+  1 reo     sysadmin    6242 Dec 14 15:16 samplefile
Putting ACLs into Practice

Consider that Department Director Laura F. is going on a 2-week vacation to a South Pacific island that has never heard of email. Laura would like to give her deputy, James K., read-only access to her mailbox so he can monitor for any emergencies. At first, the sys admin is stumped for a solution. She cannot change the group of Laura's mailbox file; all mailbox files must be in the "mail" group for the mail delivery agent to function properly:

$ ls -l /usr/spool/mail/lauraf
-rw-rw----   1 laurak   mail    3448683 Aug 21 12:10 /usr/spool/mail/lauraf
The sys admin also rules out making Laura's mailbox publicly readable, as this would risk exposing sensitive information. Two other possiblities are similarly unpalatable: adding James to the "mail" group, or giving him direct access to Laura's account. Finally, the sys admin remembers an article she read about Solaris ACLs. She uses setfacl to grant James read-only access to Laura's mailbox file:

$ setfacl -m user:jamesk:r-- /usr/spool/mail/lauraf
$ getacl /usr/spool/mail/lauraf
# file: /usr/spool/mail/lauraf
# owner: lauraf
# group: mail
user::rw-
user:jamesk:r-- #effective:r--
group::rw-      #effective:rw-
mask:r--
other:r--
Thus, functionality and security are preserved, James can monitor Laura's email, leaving the nightlong beating of native drums as Laura's only worry.

This example illustrates one of the most useful aspects of ACLs. They provide the ability to grant access to files in situations where changing existing permissions or groups would compromise security, or disrupt the functionality of an existing application.

Default ACLs and Directories

The previous example showed how ACLs can be installed onto existing files. Is there a way to specify ACLs for files that will be created in the future? The "default" ACL serves this purpose. Default ACLs are permitted only on directories, and specify ACLs that new files will inherit when created within those directories.

Here is a directory that has default ACLs:

$ getfacl sampledir
# file: sampledir
# owner: reo
# group: sysadmin
user::rwx
group::r-x              #effective:r-x
mask:r-x
other:r-x
default:user::r-x
default:group::r-x
default:mask:rwx
default:other:r-x
default:user:tdk:rw-
Note that getfacl still reports a type of "file" even on a directory.

When a file is created in "sampledir", it will automatically receive the default ACLs from its parent directory as its user, group, mask, and other ACLs.

This table shows how the default ACLs on the directory "sampledir" map onto files created in that directory:

ACLs on sampledir ACLs on new files created in sampledir

  default:user::r-x    -> user::r-x 
  default:group::r-x   -> group::r-x 
  default:mask:rwx     -> mask:rwx 
  default:other:r-x    -> other:r-x 
  default:user:tdk:rw- -> user:tdk:rw-
Note that setfacl will not allow creation of user- or group-specific defaults (e.g., default:user:tdk:rw-) unless the directory already has the four generic defaults. The next example shows the commands used to manage default ACLs.

Default ACLs in Practice

The fictitious accounting firm of Dewey, Cheetam and Howe (DCH) has arranged for several clients to transmit their weekly payroll data via FTP to DCH's Solaris FTP server. Once the data arrives, Clark, the DCH's payroll clerk, retrieves the data files, also using FTP. Because these files contain the clients' confidential data, the clients must be allowed to read and write only their own files. Additionally, Clark needs full access to all the client files.

Joe, the sys admin in charge of the implementation, has no trouble meeting the first requirement. He creates a separate directory and UNIX group for each client, setting the directory permissions accordingly. However, the clerk's need for universal access poses a problem. Because newgrp won't work with an FTP login, Joe can't simply put the Clark's account in all the client groups. However, by using ACLs, Joe can give Clark access to existing files in all the client directories, as well as any files created in those directories in the future.

$ mkdir client1dir
$ chgrp client1 client1dir
$ chmod 770 client1dir
$ ls -l client1dir
-rwxrwx---   1 root      client1    4096 Dec 14 15:16 client1dir
Before Joe can create any user-specific ACLs on the "client1dir" directory, he must first install the non-specific defaults. All four non-specific defaults (user, group, other, and mask) must be set simultaneously in one setfacl command. To make the command line more manageable, Joe takes advantage of setfacl's recognition of single-letter abbreviations of the ACL attributes:

$ setfacl -s   \
 u::rwx,g::rwx,o:---,m:rwx,d:u::rw-,d:g::rw-,d:o:---,d:m:rw clientdir
Finally, Joe adds the user-specific default ACL for Clark's access:

$ setfacl -m default:user:clark:rw- client1dir
Here are the final permissions on the directory "client1dir", as shown by getfacl:

$ getfacl client1dir

# file: client1dir
# owner: root
# group: client1
user::rwx
group::rwx              #effective:rwx
mask:rwx
other:---
default:user::---
default:user:clark:rw-
default:group::---
default:mask:rw-
default:other:---
The default ACL entries do not affect access to the "client1dir" directory itself. If Clark's account is not a member of the "client1" group, Joe will also need to add another ACL to the directory to give Clark read, write, and search access to the "client1dir" directory:

$ setfacl -m user:clark:rwx client1dir
$ getfacl client1dir

# file: client1dir
# owner: root
# group: client1
user::rwx
user:clark:rwx          #effective:rwx
group::rwx              #effective:rwx
mask:rwx
other:---
default:user::---
default:user:clark:rw-
default:group::---
default:mask:rw-
default:other:---
Whenever a new file is created in the "client1dir" directory, it will have the owner and group of the creating account, but will also automatically receive an ACL allowing Clark read and write access. Joe tests this out to be sure:

$ touch client1dir/testfile
$ getfacl client1dir/testfile
# file: client1dir/testfile
# owner: root
# group: client1
user::rwx
user:clark:rw-          #effective:rw-
group::rw-              #effective:rw-
mask:rw-
other:---
Copying ACLs

It would be tedious and troublesome for Joe to repeat the above process for each client directory. Fortunately, the -f option to setfacl provides the ability to copy the entire set of ACLs from one file (or directory) onto another. The -f option tells setfacl to read ACLs from a file, in the same format as output by getfacl.

The file name can be "-" to tell setfacl to read ACLs from the standard input. Thus, Joe can duplicate the ACLs from the "client1dir" directory onto "client2dir" and other subsequent directories with one simple command:

$ getfacl client1dir | setfacl -f - client2dir
More ACL Uses

ACLs are also handy in delegating selected systems administration tasks. This capability is especially useful in certain environments that prohibit the use of non-commercial tools such as sudo.

For example, a common task to delegate is maintenance of the /etc/aliases file. If available, sudo can be used to permit a non-root user to invoke an editor on the /etc/aliases file. However, this could be a serious security problem. Most editors allow shell escapes and the execution of arbitrary commands, which would execute at root privilege. This would bypass sudo security. A more secure alternative would be to add an ACL to /etc/aliases to allow the user to edit the file directly, with no need for root privilege.

The fine-grained access control afforded by ACLs can be helpful to ordinary users as well. For example, Fred needs to allow Carl to access files in Fred's home directory, but Carl is not a member in the group that owns Fred's home directory. Without ACLs, Fred would have to make his home directory world readable and searchable for Carl to access it. However, using an ACL, Fred can grant full access to Carl alone, without opening up Fred's home directory to everyone on the system.

Say What?

Even after so many years, UNIX programmers still have not learned how to give their programs good table manners. The ACL management utilities are no exceptions. So, here is your secret decoder card for some of the most common crypto-errors from setfacl.

setacl error: Invalid argument
Root is no longer omnipotent. Only the owner of a file or directory can add or change ACLs. If root tries to set an ACL on a non-root file, setfacl will respond with this very helpful error message.

Missing user/group owner, other, mask entry
You used the -s option, but did not include the required user::, group::, and mask:: entries.

Another cause of this message is attempting to add a user or group-specific default ACL (e.g., default:user:reo:r-x) to a directory when no non-specific default ACLs (e.g., default:user::r--) exist.

non-digit in id field
The user or group name in an ACL entry does not exist. If the name is not found in /etc/password or /etc/groups, setfacl will try to interpret the field as a numeric ID.

Unrecognized character found in mode field
setfacl found a character other than r, w, x, or - in the permissions field. The message itself is not so bad, but it would be much more helpful if setfacl specified which position held the incorrect character.

Some parsing errors will cause setfacl to exit without producing any error message. If setfacl is silent, but does not appear to have done what you intended, check the exit status. If it is non-zero, a parsing error may have occurred. One particular error I noticed that will cause this behavior is an extraneous colon character in certain ACLs, such as "other::rwx".

Conclusion

Access Control Lists augment the traditional UNIX file permissions scheme to permit more flexibility and fine-grained control over file access. ACLs are especially useful in situations where changing or adding groups is not possible. Systems administrators no longer need to resort to convoluted arrangements of multiple groups and set-UID scripts to achieve the best balance of utility and security. While ACLs may not be everyday tools, they are an excellent addition to every Solaris sys admin's bag of tricks for those occasions when the standard UNIX permissions just aren't enough.

Ross Oliver is Chief Technical Officer for the information security consulting firm Information Warehouse! Inc. His 15 years in the high-tech industry has included positions at E*TRADE, Sun Microsystems, SGI, SCO, and Taos. When not flying a keyboard, Ross can often be found navigating his Zlin 242L aircraft through California skies. Ross can be reached at: reo@iwi.com.