Well it seems to be a popular topic lately. First of all lets clarify my statement on subject tranquility I made previously. As stated before, the problem with the lack of tranquility comes from policy not reflecting what is being enforced (due to revocation) and the introduction of time into analysis and design. Note that int SELinux, revocation issues can also result from policy reloads, not just relabeling of subjects/objects directly.
What about dynamic transitions?Dynamic transitions are an evil hack to add support for legacy applications that are even bigger hacks! Domain transitions occur on the execve call, that is before a process is actually created. The process keeps this label as long as it is running (tranquility). Subject tranquility is maintained throughout the duration of a process as long as dynamic transitions are not used. Dynamic transitions are capable of changing the type of a process at any point during execution. Talk about an inconsistent state. Introducing dynamic transitions is wrong and a hack! In addition to adding the complexity of time to design and analysis it opens a whole new can of worms. The address space is the same for the process regardless of it’s label. So let’s say you’re using a dyntrans b/c you only trust a small segment of code to run in a certain privileged domain. An exploit could very easily result running untrusted code in the privileged domain. It would be a trivial exercise; just overwrite the return address of a function to return to somewhere in the untrusted segment and bam…the untrusted code is running in the privileged domain. If dynamic transitions are evil shouldn’t all domain transitions be evil since they destroy tranquility?
No beause normal domain transitions maintain tranquility (ed note: this is actually not correct, please refer to the comment1 and comment2 below… however the content of this section is still valid wrt time, just not wrt to relabeling) As stated above, the transitions happen on the execve call and the type is kept for the lifetime of the application. This means there is the complexity of time is never introduced. When one process starts another process one of three things can occur:
- no transition will take place (the child runs in the parent’s domain)
- an automatic transition will specify the domain of the child process
- the parent will set the context of the child before the execve() takes place
The first one is self explanitory. The second can easily be analyzed and determined (a parent/executable combination can only automatically transition to one child domain, any more is a policy error). The third is interesting because it permits a parent application to choose the destination domain for it’s child. This is useful for things like login processes where a single application might need to start up children in different domains (ie login running bash in separate domains depending on the user). Applications permitted to do this are considered trusted in this regard and are kept to a minimum. Even though these applications are “trusted” policy still governs the transitions permitted. Going back to the previous example policy must permit login to run bash in a certain domain; login can’t just pick any arbitrary domain. Regardless all three maintain tranquility. Policy never reflects something that is not being enforced (revocation) and the complexity of time is never introduced.
OK so now everyone is clear on subject tranquility in a TE environment right? So what about mmapd files and shared memory? In the case of mmaping can we just relabel files since revocation is fairly instantaneous?No! Bad! There is no current way to relabel a shared memory segment but we still have to deal with policy reloads. With mmap’d files we have to deal with both relabeling of the underlying file, and policy reloads. However before we get into that, lets talk about why there are revocation issues in the first place. The short of it is that once a segment of memory has been mapped into your processes space than the kernel doesn’t do much else. Remember, you can treat shared memory segments and mmapd files as any other piece of memory, read to it, write to it, execute it, etc (all depending on how you map it). The kernel doesn’t perform any types of check beyond ensure that you stay in your own space. Can you imagine the overhead introduced if access control checks occured everytime you tried to access memory.
So the relabeling problem with mmap’d files is that once a file has been mmap’d, changing the label will have no effect on the processes that have it pinned in memory. So you may think your shutting down your pipeline by relabeling files, or changing the security properties of an mmap’d file shared by several processes by changing the label, but it doesn’t work at all.
Now in the case of shared memory and mmap’d files across policy reloads the same revocation problems exist. If the first policy allowed the access, the second policy can’t revoke it. As an aside, when any relabel/policy reload is performed, time is an issue. How the heck can you analyze policy when one moment the policy means one thing and the next moment it means something else? At least with reloads you can analyze the first policy, the second policy, and perhaps the state of the system at time of reload to understand everything (ignoring the revocation problems described above….) As for relabeling, a few trusted applications must relabel for administrative purposes. RPM, emerge, and other package managers must be able to properly configure the system. Can I just relabel a parent directory and expect a revocation to take place on all files contained within that directory?No! Bad! Permissions on parent directories are not checked with every read() and write(). Imagine the overhead this would introduce. Directories are only used to lookup an inode, once that inode is obtained the directory is worthless. What about “mv” and the other coreutils? Don’t they break the tranquil model?
<tangent>I was exploring the commands in the coreutils package and encountered some interesting facts. The “mv” command may require varying permissions to complete successfully. If the source(s) and destination are on the same filesystem one set of permissions are required (a rename operation technically). If the source(s) and destination are on different filesystems then a different set of permissions are required (basically a copy then unlink). This is fairly intuitive but I had never thought about the varying codepaths (and thus varying security hooks) for the mv command.</tangent> Regardless the source security context is maintained (as long as you’re running a recent version of coreutils with the appropriate SELinux patches). This maintains object tranquility. You still must be aware that moving a file to another location on the same filesystem still provides an open writer with access to that file. The inode is still valid and the security context is not changed. Moving a file to another filesystem may preserve the security context, but the inode will no longer be valid. The writer could write() as much as it wanted after the mv, but as soon as the file is closed it will disappear in a puff of smoke. OK, maybe not a puff of smoke, that depends on a few other things…
feed
Comment by Joshua Brindle
1 Thursday, July 13, 2006, 6:23 pm o'clock |
This is incorrect. The process address space is created by fork(), the execve overwrites the application data only, not the environment (glibc atsecure removes some environment variables that affect the linker) but there is a significant chunk of data that is non-tranquil. Imagine doing a domain transition on a perl script, how many environment variables can affect the execution of the perl script that won’t be cleansed across the execve().
Also erroneous. By definition when revocation occurs the policy says one thing at one moment and another thing at another moment. The only affect here is a possible delay between when the old policy is enforcing and when the new policy takes affect.
Granted there are revocation issues but you aren’t covering any of them here. The primary revocation issue is memory, shared memory and mmap()’d files are not able to be revoked when a new policy is loaded.
Comment by spencer
2 Friday, July 14, 2006, 6:45 am o'clock |
True. I probably should have corrected this. It should read: Yes evil but necessary in the Linux environment since the environment (et al) is relabeled during the transition. One way around this would be to do a full copy of the memory space on exec but I doubt the kernel maintainers would be interested. I’ll leave the old content in take but add a note to this effect.
Yup shoulda went with this example anyways…. thats the more interesting case.
Notice both the subject tranquility and mmap/shm issues that crop are due to the memory susbsystem. Someone correct me if I’m wrong but SELinux could have maintained subject tranquility if the entire task struct was copied instead of overwritten on execve. All the data could have been copied to the new task struct, but the new task struct would contain the new sid. Given the lightweight nature of Linux process fork/execs I don’t it is worth the tradeoff.
Still, it is a relabel by definition thus violating the laws of tranquility but at the same time, I would argue, presents no revocation issues and doesn’t introduce the complexity of time into analysis. We trust the kernel to not begin execution of the program until the security context is correct. Without being a kernel expert, I would assume other processes on the system can’t interact with this process until the kernel actually runs it, and intuition tells us that a non-running application can’t really initiate any type of communication. Thus, during analysis we assume that the process and all it’s “metadata” has that label for it’s entire lifetime. Still a relabel is a relabel, one second the policy means one thing about a segment of memory, and the next it says another.
As for the mmap/shm issue I’ll actually update the entry to avoid scaring people about file relabels…