One of the intended uses of my OpenSolaris storage server was to serve as a "SAMBA":http://www.samba.org accessible data store. Part of that role was the wish to have an incoming
directory modeled after similar directories found on many FTP servers. In detail this meant a share with the following properties:
- Readable for everyone (including unauthenticated users, i.e. guests)
- Everyone can create new files and directories on the share
- Only certain users can delete files and directories from the share
So everyone can add files to the share, but removing them requires special privileges.
It turns out that this is impossible to do with normal UNIX file system permissions, as for UNIX creating a file (which is a write operation on a directory) is much the same as deleting one (which is a write operation on a directory).
Fortunately OpenSolaris supports a much more powerful file operation permission language in the form of NFSv4 permissions.
It has been said that the NFSv4 permission system has been modeled after a smudged copy of the Windows NTFS permission system, and there is certainly merit to that claim, which is not a bad thing. The NTFS permission system is much more expressive than the standard UNIX system, as it has more actions (besides writing, reading and executing it also knows about deleting, for example), can support a large number of principals with different permissions and can actively deny an action (which is different from "not allowing").
h3. NFSv4 permissions
The NFSv4 system knows about the following actions:
|_. Action |_. Description for files |_. Description for directories |
| read data | Read file contents | List directory contents |
| write data | Write file contents (anywhere in the file) | Create new files |
| execute | Execute file | Change into directory |
| append | Append data to file | Create new directories |
| delete | Delete the file | - |
| delete child | - | Delete a file in the directory |
| read/write attributes | Read/write basic attributes | (same as file) |
| read/write xattrs | Read/write extended attributes | (same as file) |
| read/write ACL | Read/write ACLs | (same as file) |
| change owner | Change the owner | (same as file) |
| sync | Use syncronous file access | - |
NFSv4 also contains a mechanism to specify actions that apply to a file or directory, and actions that are inherited to child objects of a directory (i.e. files or subdirectories). This allows very fine grained control of file system access.
Of special interest here are the bits about writing, appending and deleting files and folders.
The ACLs are maintained in a list of entries, each entry mapping a username/action pair to a verdict (allow/deny). Each access is matched against each entry in
turn, and the verdict is taken from the first entry to match. So the order of entries is important.
Solaris' ls
has two extensions to list those ACLs: -v
for a verbose listing and -V
for a concise listing. The format used by -V
can be passed to chmod
to change ACLs.
The permissions corresponding to the list of requirements stated above are as follows (/tank/share/incoming
is the directory associated with the incoming
share in smb.conf
):
# ls -lVd /tank/share/incoming
drwxrwxrwx+ 5 root root 6 Dec 12 16:49 /tank/share/incoming
user:sun:-w--dD--------:fdi----:allow
user:sun:-w--dD--------:-------:allow
everyone@:-w--dD--------:f-i----:deny
everyone@:----dD--------:-di----:deny
everyone@:----dD--------:-------:deny
everyone@:rwxp--a-R-c--s:-di----:allow
everyone@:r-xp--a-R-c--s:f-i----:allow
everyone@:rwxp--a-R-c--s:-------:allow
#
There are two kinds of entries in this list. Those with an i
in the second part of the action list and those without. The entries with an i
are so called "inherit only" entries. They do not apply to the file or directory they are associated with, but are only inherited to new child entries. The other entries apply to the file/directory they are associated with.
This list can be read in three blocks:
The first block consists of the first two lines. The first line specifies that the right to delete files (d
), delete child entries (D
) and create new files/write file content (w
) for the user named sun
is inherited to new files and directories (fdi
). This makes sure that this user can always remove files and directories, and overwrite existing file content in newly created files. The second line applies the same rights to the incoming
directory itself.
The second block consists of lines 3 to 5 and contains only deny statements. They apply to everyone@
, which means exactly what it says on the box. Lines 3 and 4 again deal with rights that are to be inherited to child objects, but the rights inherited to files and directories are different this time. Files inherit a deny to write anywhere in the file (w
) and file deletion (dD
). Directories just inherit the deletion part, otherwise new files could not be created in subdirectories (which needs the w
right). The incoming
directory itself gets the "no deletion" treatment as well.
The third block consists of the last three lines and restores some rights to non privileged users. Directories inherit the right to be read (r
), changed though (x
), new files and subdirectories can be created (rp
), and attributes of all sorts can be read (aRc
). We also allow synchronous file access (s
). Files are much the same, except that the write anywhere right is missing. Not that it would matter much if that were allowed here, since it has been explicitly denied earlier. Note that the right to append to a file (p
) is explicity allowed. The rights for the incoming
directory itself (last line) again match those inherited to subdirectories.
Let's see if that works out.
$ id
uid=60003(smbnobody) gid=60003(smbnobody)
$ touch /tank/share/incoming/foo
$ ls -V /tank/share/incoming/foo
-r-xr-xr-x+ 1 smbnobody smbnobody 0 Dec 12 18:33 /tank/share/incoming/foo
user:sun:-w--dD--------:------I:allow
everyone@:-w--dD--------:------I:deny
everyone@:r-xp--a-R-c--s:------I:allow
The unprivileged user smbnobody
(SMB guest access is mapped to this uid) can create a new file in the incoming directory, and the file inherits the rights mentioned above (I
signifies an inherited right).
$ cat /etc/passwd > /tank/share/incoming/foo
bash: /tank/share/incoming/foo: Permission denied
$ cat /etc/passwd >> /tank/share/incoming/foo
$
The user cannot overwrite the file (even though it is empty), but he can append to it.
$ rm /tank/share/incoming/foo
rm: /tank/share/incoming/foo: override protection 555 (yes/no)? y
rm: /tank/share/incoming/foo not removed: Permission denied
$
Deletion is also denied. Good.
$ id
uid=500(sun) gid=100(users)
$ cat /etc/passwd > /tank/share/incoming/foo
$ rm /tank/share/incoming/foo
$
However, the privileged user sun
can overwrite and delete the file.
h3. Samba configuration
Samba also needs configuration to recognize and use the extended parmission system. The following is an excerpt from smb.conf
, describing the incoming
share:
[incoming]
path = /tank/share/incoming
writable = yes
guest ok = yes
browseable = yes
public = yes
acl check permissions = False
ea support = yes
store dos attributes = no
map readonly = no
map archive = no
map system = no
map hidden = no
vfs objects = zfsacl
nfs4: mode = simple
nfs4: acedup = dontcare
This configures Samba to use extended ACLs using the ZFS (NFSv4) permission system.