How to configure file access policies -------------------------------------------------------------- Sysmask uses text based file access control method, checking directly the pathname of the access request submitted by the process (or the user) instead of the physical location of the file. This method has the advantage that the definitions are simpler with wildcards and more human-readable, the policies can be defined in a centralized location (which is much easier to secure), runtime modifications can be made with immediate effect and no need to restart the process, and that it can filter badly formatted pathnames before they dive deeply into the kernel's drivers. Such badly formatted pathnames might be an attempt to exploit a bug or trojan horse hidden in the drivers. On the other hand, care must be taken when defining access control rules in order to avoid pitfalls, especially when wildcards are used in the definitions. There are three basic masks for selective file access control: ropen, wopen, exec. Respectively for read, write and execution accesses. The policies are defined in the file "open" for ropen and wopen, and "exec" for exec, in the token's configuration directory. The access restrictions are added up to the usual filesystem access checks. So a file that is normally non-accessible to a process will not become accessible under sysmask. There is also an auxiliary mask, "path". If set, the kernel will use the "open" definition to check every pathname submitted with system calls, before diving into path_lookup. This mask must be set, and access control appropriately configured, if you want to filter badly formatted pathnames. --------------------------------------------------------------- First of all, the access control definition is based on the "first match" principle, that is, it is the first line in your configuration file that matches the pathname of the request that counts, and subsequent lines for matching rules are no longer checked. This means that the order under which you put matchine rule lines and blocks into the configuration file is very important. A proper ordering allows you to define nested inclusion/exclusion in a very simple way. For example, suppose that you want to allow a process to read everything in /my/home/document except /my/home/document/secret/, but nothing else in /my/home. You can do the following. ----------------- /my/home/document/secret /my/home/document/secret/ access= /my/home/document /my/home/document/ access=r /my/home /my/home/ access= ------------------ Note that "/my/home/" matches everything under /my/home, except the directory itself. Therefore you need the line "/my/home" to deny directory searching on your home. This example contains 3 blocks. Everything in /my/home/document/secret and those in /my/home but not in document will match either the first or the third block and get refused. Things in /my/home/document but not secret match the second block and are accepted. However, this example is over-simplified, in that if there is a symbolic link in /my/home/document pointing to a file in the secret subdirectory, your secret could be accessed via this symbolic link. Therefore, you have to know how to close such loopholes. --------------------------------------------- To simplify things a little bit, we can think of the "access" definition of a block of consisting essentially of two choices: allow or deny. Under this simplification, blocks can be classfied into allow blocks and deny blocks. In general, if you put only explicit file names without wildcards into the matching rules of an allow block, there is no major loophole. Because the files you define in the block can only be accessed via the names defined by YOU. Well, under the condition that the process has no right to change the file, for otherwise it could replace it by a symbolic link. On the other hand, explicit file names in a deny block doesn't worth much if there are allow blocks in the same configuration file having wildcards in the matching rules, and care is not taken in the LATTER BLOCKS to close loopholes. The point is, the process under control, or even another process attacking your system in coordination with the controled one, has several possibilities to use another file name to access the prohibited file. 1. The simplist possibility is to copy the prohibited file into an allowed directory, in the case of a coordinated attack by several processes. For this reason, file write access should be very strictly controled for untrusted processes. 2. Then comes methods to access files under a different name: symbolic and hard links, and rename(). If you grant writing to a directory, the process could create a symbolic link there, pointing to ANY file in your system. So you must take one of the following actions to prevent this. a. Set the link mask. This is simple and efficient, and for most untrusted processes you can do so. b. Set the wopen mask, and use call conditions on blocks giving writing access, denying operations link, symlink and rename whenever you can. Of course you can use both, but b is void if link mask is set. Either one is enough to prevent the process itself to create uncontroled file linking or renaming. However, none prevents the process from following an already existing link. The last point is rather a feature than a bug, because you yourself may want to use this feature to create controled accesses. If this is not the case and if you don't want the process to follow ANY links and get to prohibited areas, then you should set the "follow" option and the "hardlink" denial. The "follow" option forces a pathname rechecking after a symbolic link is followed, on the real path of the file. It affects every operation that modifies the file system as well as open() for read, and should of course be installed on allow blocks. Installing this option on a deny block serves nothing. The following operations are not affected by "follow": stat, lstat, access, chdir, readlink. The "hardlink" refusal is to control accesses to hard links. This time one cannot tell which file is the original and which is the copy, so the hardlink refusal can only refuse EVERY file that has hard links on it. Once again, it should be installed on allow blocks. As a big source of human error is to try to install "follow" and "hardlink" on link targets (who are deny blocks), it is highly recommended that they be set on the whole file access configuration, and the link mask be set too, whenever this is possible. There is sometimes a difficulty to set global hardlink refusal, due to some hard lined system files such as /bin/zip or /usr/bin/zgrep. For ordinary users, practically the only meaning of such hard links is some economy on storage space for identical files. However, the very marginal gain on today's disk space (100-200MB) simply doesn't worth the security plus of a global hardlink refusal. So you can use the script /usr/sbin/hl2copy included in this package to replace the hard links by file copies. 3. As for the plain old directory backup trick "/../", if "follow" is set and if the follow_dotdot definition in /etc/sysmask/smkdrc is set to yes, there should be no risk from this. You can also brutally deny directory backup by setting the "backup" refusal, or limit the depth of backups. Otherwise, you must be aware of possible loopholes that can be created by combined use of symbolic links and directory backups. For example, if dir_b is a symbolic link pointing to, say, /usr, then the following points to /etc. /dir_a/dir_b/../etc While if follow option is not set, sysmask only checks the access rights for /dir_a/dir_b and /dir_a/etc. 4. If a process running as root has the right to mount a filesystem, it can remount a divice to another place, thus escaping the file access control. The best solution is to set the mount mask to prohibit this. If ever this is not possible, use condition lines on the type of operation to restrict the access targets of mount (for both devices and mount points). 5. And a process running as root may also use chroot to escape name based access control, especially for explicit access denials. The best solution to prevent this is to set the chroot mask; when this is not possible, chroot refusal can be used in the configuration. Condition lines can also be used. --------------------------------------------- There are also two special tools to help file access control, based on the traditional unix uid system: the denyid mask, and the checkuid action. Here the denyid mask is in general reserved for special system uses. In the system, one can define a user named smk-deny. Once the mask is set for a process, any file or directory belonging to this user will be unaccessible, even if the process is run as root, or if the access bits of the file tells otherwise. The protection is valid across links, remounts and chroot, but as it cannot be selectively configured process by process, its use is normally limited to protecting vital system files, such as the sysmask system level configuration, or /etc/shadow once that makes sense. And the checkuid action can also be used by ordinary users to simplify access control rules at the user's level. It makes the kernel to check the file's accessibility by another user. You can use this action in the access configuration file to allow access only to files that can be accessed by nobody for example (besides being accessible by the usual process uid-gid conditions). For daemons running as root, this closes access to everything only readable by root; for a user level program, this closes access to files in the user's directory that are not readable by others. ----------------------------------------------------------------- The executability of files is another pitfall, this time not specifically for sysmask. The point is that under Linux, once a file is readable by a process, it could be executed. For example, a sh script can simply be sourced by sh if it is readable. A binary file can be copied then chmod()ed to executable if chmod mask is not set. Or simply /lib/ld.so can be executed to load the binary, even if the process has no right to chmod or to open for writing. The point here is not that /lib/ld.so should not be allowed to execute anything readable. If /lib/ld.so can execute readable binary files, other codes might do it too, including exploits of vulnerabilities. This is a design flaw of the system that is not that easy to patch. Therefore, DO NOT rely only on the exec configuration to exclude files from executability! If you want to exclude some files from execution, you had better also make them non-readable by the process. And as a general matter, DO NOT think that people cannot execute a file if it is not marked executable! --------------------------------------------------------------- If you do not want a process to access devices and/or the /proc pseudo filesystem, do not rely solely on file access configuration. Device files could be created by mknod, and proc filesystems might be mounted somewhere else without you knowing it. Whenever possible, set the dev and/or procfs masks.