summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBas Wijnen <shevek@fmf.nl>2006-06-14 08:43:53 +0000
committerBas Wijnen <shevek@fmf.nl>2006-06-14 08:43:53 +0000
commitf90de21cf7b88413e92a5220a35fe6ba4dc53171 (patch)
tree74ab3f600e80ca2ad1ef756dd32a942a216849fa
parent1a51bca961c1d320ffad5f6c429046743c743b43 (diff)
none
-rw-r--r--Hurd/TrivialConfinementVsConstructorVsFork.mdwn6
1 files changed, 4 insertions, 2 deletions
diff --git a/Hurd/TrivialConfinementVsConstructorVsFork.mdwn b/Hurd/TrivialConfinementVsConstructorVsFork.mdwn
index a5e9371f..4d221a8e 100644
--- a/Hurd/TrivialConfinementVsConstructorVsFork.mdwn
+++ b/Hurd/TrivialConfinementVsConstructorVsFork.mdwn
@@ -42,13 +42,15 @@ POSIX fork, or rather fork+exec, is how things are done on many current systems.
Fork is bad. The following text should convince the reader that we do not want to use fork. If it fails to do so, please write your reply to <l4-hurd@gnuNOSPAM.org>. We can then improve this text, either by using better arguments, or by saying that fork is acceptable after all. :-)
+First of all, it must be clear that we are using a capability-based system. This offers a lot of security that POSIX doesn't have. The actual solution presented below doesn really work on POSIX, because C can simply reopen all the files. After all, it is running as the same user, with all the same rights, as P. This is not the case in a capability based system. Every process needs a capability to do things. The closest to the POSIX concept of a "user" is a bunch of capabilities to all objects that the user is allowed to handle. However, even if P has all those capabilities, that doesn't mean C does as well. So for example, if P doesn't give a capability to the user's home directory (and it probably will not do that indeed, but it'll give a private part instead which C can use for files), then C cannot get access to the files in there. And so in particular, if P has a capability to ~/.ssh/id\_dsa, and it doesn't give it to C, then C cannot simply call open and get the capability from there, because the file isn't in its file system.
+
The big difference between fork and the other options is capability B. B is a private capability of P. P does not want it to be passed anywhere. In all cases this is achieved. However, fork needs to be explicit about this. If P (or actually P') forgets to drop B, everything will still work (C didn't use B anyway). However, if C contains a security bug and is taken over by a cracker, then that cracker has access to B. This means that due to a simple mistake, the concequences of a compromised C are bigger than they need to be. This problem is of course even bigger if C is untrusted code in the first place, because it doesn't even need to be "taken over" then, it may simply be malicious.
In contrast, the other two options don't pass anything by default. If there is a similar mistake there, they would forget to pass A to C. That will soon be noticed, because C actually needs A (otherwise it shouldn't receive it). So C will fail to work. This will quickly be fixed, resulting in a better program.
## <a name="Solving_the_problem"> Solving the problem </a>
-The problem of exec can be solved. It is if the default would be to not pass capabilities to the new process by default, but specify a list of capabilities that it should keep. However, in that case the only difference with trivial confinement is that P' dies in the process (and thus must be created to prevent P from dying). Almost any use of exec is in practice preceded by a fork for this purpose. It would be easier to make trivial confinement the default operation and let P die directly after it in the rare case that it should.
+The problem of fork+exec can be solved. It is if the default would be to not pass capabilities to the new process, but specify a list of capabilities that it should keep, or (like in the other cases) pass them over a new channel which is implicitly created during the fork. However, in that case the only difference with trivial confinement is that P' dies in the process (and thus must be created to prevent P from dying). Almost any use of exec is in practice preceded by a fork for this purpose. It would be easier to make trivial confinement the default operation and let P die directly after it in the rare case that it should.
The only reason for continuing to use fork+exec would be that it is what existing programs do. However, they break anyway if they need to specify which file descriptors to pass. So they need to be adapted. Therefore, it's better to make the usual spawning method the primitive one, and emulate the other.
@@ -56,7 +58,7 @@ The only reason for continuing to use fork+exec would be that it is what existin
Note: the following has not been extensively discussed on the mailing list, and no consensus has been reached AFAIK. This is the personal opinion of Bas Wijnen.
-The difference between trivial confinement and the constructor is one of control. With trivial confinement, P is in full control of the process. For example, if P is a debugger, it could choose to put some breakpoints into C before starting it. With the constructor, this control lies with P1. However, P2 is likely the one who will want to use the debugger. The constructor is explicitly designed to allow this type of control by the programmer (or system administrator) over the user.
+The difference between trivial confinement and the constructor is one of control. With trivial confinement, P is in full control of the process (and since P is under full control of its own parent, that parent also fully controls C, and the parent's parent as well, etc. Note that the chain of parents is usually short). For example, if P is a debugger, it could choose to put some breakpoints into C before starting it. With the constructor, this control lies with P1. However, P2 is likely the one who will want to use the debugger. The constructor is explicitly designed to allow this type of control by the programmer (or system administrator) over the user.
In the Hurd we want to enable the user to do these sort of things. We specifically don't want the administrator to use such control. So we do not need to provide the means for it in our system. (Note that not using a constructor doesn't actually guarantee that this kind of control is impossible.)