Messages in this thread | | | Date | Sun, 26 Apr 1998 01:27:11 +0200 | From | Andrej Presern <> | Subject | Re: [patch 2.1.97] more capabilities support |
| |
Alexander Kjeldaas wrote: > As I said, I don't have anything against pure capabilities. However, I > don't see pure capabilities being implemented in Linux this year and > not next year either. That makes me conclude that the discussion on > "pure vs POSIX" capabilities is a non-issue. One of them has been > implemented and will improve security, the other one is at most at the > design stage. > > The pragmatic in me tells me that the software that will actually > _use_ the POSIX capability interface will be quite low. So if you, in > 3 years time, come up with an implementation of pure capabilities in > Linux it will not be a huge task to change a few programs to use a new > interface. It won't even be difficult to support the old interface. > > _That_ is a win-win situation.
Theodore Y. Ts'o wrote: > > Date: Thu, 23 Apr 1998 20:51:31 +0200 > From: Andrej Presern <andrejp@luz.fe.uni-lj.si> > > Besides, I really don't see a good reason why a clearly superior design > should not be implemented, especially considering the fact that both ACL > and capability list designs can be implemented on top of it, ensuring > compatibility with existing standards and old programs. Pure capability > based systems have been running commercial and military security aware > applications for over 20 years and have even been implemented on off the > shelf technology (such as IA32) for a number of times. > > How about the fact that in order to do pure capability systems, you'd > have to rev the interface of every single system call to take a > capability argument (just as file I/O syscalls now take a file > descriptor)?
Not neccessarily.
I know that requests for massive changes of the kernel won't even be considered and I wouldn't even think of proposing the idea if I didn't think that massive changes are actually not required to at least minimally support pure capabilities.
The path that I would propose is to add minimal support. Currently, processes can assume that they can call every non privileged system call - those that the process actually needs and those that the process doesn't need also. To get the ball rolling, I would propose a modification of this mechanism, so that a process is able to restrict itself to only a subset of system calls, thus throwing away the superfluous authority that can be abused if the program contains a bug.
An example use of such a basic and primitive extension would be that a suid program can throw away its authority to execve() system call, if it doesn't need it. This alone would make it quite difficult for the hacker to produce a root shell by means of stack smashing for example, and would make it difficult to do so for all time, not just until the next bug in argument handling code (of the program or libraries) is found. If the program would actually need such authority, it can get a copy of the existing capability (which is all syscalls by default for beginning), modify it so that it drops execve() and then use one of the two capabilities as appropriate in sections where execve() is needed and where execve() is not needed, thus restricting the amount of risky code arbitrarily (the application can install the with_execve() capability just before execve() is called and install without_execve() capability where processing user arguments for example).
Of course, it would be much better if such capabilities could be attached to the program image residing on the disk. In this case the suid app would not need to be changed at all because the syscall capability would be loaded from the disk at startup time automatically, plus we could also prevent a troyan login program (that wouldn't want to throw away authority by itself) from storing intercepted passwords on the disk by not giving it the ability to do write() (another stupid example). But this would require changes to the file subsystem, which means quite a bit of changes so I don't think its appropriate right now.
The reason why such an extension would be usefull lies in the principle of the least authority. If the program doesn't have the authority it doesn't need, such authority cannot be abused. Not now, not ever.
Of course, because Linux is not a pure capability system by default, such an extension would have some impact on the system. If we don't want to make it so that the process has to pass the syscall capability at call time, the change to IDT (or the system call table or the system_call function) would need to be made at process switch time. The other impact would be in the use of kernel memory, because every different syscall capability would require some storage. Although only a few running tasks would want to have a modified syscall table, such tasks would eat up a few hundred extra bytes of non swappable memory for their private syscall table which would impact all running programs.
On the other hand, POSIX capabilities also take up extra space. They do so with programs residing on the disk, and they do so with running tasks, the difference with pure capabilities being that pure capabilities only take up space if they are given to an object while POSIX capabilities do so always.
If the whole sys_call_table would need to be allocated that will hold an alternative syscall mask, that would mean approximatelly 173*4 bytes (if I recall the current number of system calls in 2.1.x kernels correctly) wasted by every process that wants to hold different syscall sets, plus a small modification of system_call in arch/x/kernel/entry.S so that it is possible to change the location of sys_call_table (a pointer is needed instead of hardcoding it into the kernel). Unfortunatelly, some means would be needed to perform the change of the sys_call_table for the current process and I am not sure if it would be better to use a new syscall or to do it in some other way. Because we're still in the beginning and want to keep changes small, we won't make this capability passable in any direction (not even parent -> child), so we can free alternative sys_call_tables when the process that requested them exits.
So, for a slow start, I would like to see these pure capabilities in the system: 1) create a duplicate of current sys_call_table and return it to me 2) disable a syscall in the current sys_call_table 3) install a sys_call_table
This way, the process can't get any more syscall authority than it has by default (making room for future extensions), but it gets the ability to disable potentially dangerous system calls that could be abused if the application is invaded by some foreign code (ie as with stack smashing).
I will try to write some code that does this, but I hope I won't mess something up in the kernel, because I'm still a newbie to the Linux kernel source:)
> You could set a global capability for the process, and switch between > capabilitiy sets, but at that point you've lost most of the advantages > of a pure capability system. (You might as well raise and lower POSIX > capaibilities.)
Switching between capability sets is not pure capabilities, it's capability lists, ie. POSIX capabilities. If authority can be attached to a program flow instead of the program (that is, it can be passed between objects), you don't have to switch but only always use the same authority that has been given to you by the object at the other end of the input channel. If an operation cannot be performed it's because input object didn't give you enough authority.
+-- object border -----------------------+ | | obj1 -+-> exec. flow with auth. given by obj1 -+-> output 1 | | obj2 -+-> exec. flow with auth. given by obj2 -+-> output 2 +----------------------------------------+
If the authority is not separated from the object, the object must switch between different sets of authority within itself. In this case, the object is responsible for making a distinction between input objects 1 and 2. Input objects 1 and 2 are the same at the border and it's up to the program to make a distinction between them and use propper authority, making program more complex as the number of different authorities rises.
+-- object border -------------------------+ | | obj1 + | +-- obj1 ------------------+-> output 1 +-+-> exec. flow -+ (auth. of obj1 or obj22?)| obj2 + | +-- obj2 ------------------+-> output 2 +------------------------------------------+
Anyway, because in the end it always comes to Linus saying yes or no, I would like to hear what Linus thinks about the idea. I don't want to waste any more time of any of you (and mine) trying to explain something that is not even going to be considered (for whatever reason). I am aware that what I am proposing is in the end not a small thing. I am also aware of the fact that my current knowledge of Linux internals is not sufficient to be able to provide a complete design for Linux that will prove the concept, which has been asked of me for a number of times. I have not yet heard any opinions, arguments or comments from neither Linus nor Andrew Morgan who also expressed interest, so I guess this is what I am asking for right now.
Andrej
-- Andrej Presern, andrejp@luz.fe.uni-lj.si
- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu
| |