10 |
|
|
11 |
\begin{document} |
\begin{document} |
12 |
\maketitle |
\maketitle |
13 |
|
\newpage |
14 |
|
\tableofcontents |
15 |
|
\newpage |
16 |
|
|
17 |
\section{Introduction} |
\section{Introduction} |
18 |
|
|
127 |
architecture of course. We might want to have a more architecture |
architecture of course. We might want to have a more architecture |
128 |
independent way to pass the information about further modules to |
independent way to pass the information about further modules to |
129 |
the rootserver. We also might want to gather the information |
the rootserver. We also might want to gather the information |
130 |
provided by GRUB in a single page (if it isn't). |
provided by GRUB in a single page (if it is not). |
131 |
\end{comment} |
\end{comment} |
132 |
\end{itemize} |
\end{itemize} |
133 |
|
|
224 |
initial task in a generalized manner. |
initial task in a generalized manner. |
225 |
|
|
226 |
\begin{comment} |
\begin{comment} |
227 |
The exact number and type of initial tasks necessary |
The exact number and type of initial tasks necessary to boot the |
228 |
to boot the Hurd are not yet known. Chances are that this list |
Hurd are not yet known. Chances are that this list includes the |
229 |
includes the task server, the physical memory server, the device |
task server, the physical memory server, the device servers, and the |
230 |
servers, and the boot filesystem. |
boot filesystem. The boot filesystem might be a small simple |
231 |
|
filesystem, which also includes the device drivers needed to access |
232 |
|
the real root filesystem. |
233 |
\end{comment} |
\end{comment} |
234 |
|
|
235 |
|
|
236 |
\section{IPC} |
\section{Inter-process communication (IPC)} |
237 |
|
|
238 |
The Hurd requires a capability system. The current L4 specification |
The Hurd requires a capability system. Capabilities are used to proof |
239 |
supports the notion of a redirector, that can be set for a task by the |
your identity to other servers (authentication), and access |
240 |
privileged threads and forces all IPC through a different thread that |
server-side implemented objects like devices, files, directories, |
241 |
can then define the policy for IPC. |
terminals, and other things. The server can use a capability for |
242 |
|
whatever it wants. Capabilities provide interfaces. Interfaces can |
243 |
|
be invoked by sending messages to the capability. In L4, this means |
244 |
|
that a message is sent to a thread in the server providing the |
245 |
|
capability, with the identifier for the capability in the message. |
246 |
|
|
247 |
|
Capabilities are protected objects. Access to a capability needs to |
248 |
|
be granted by the server. Once you have a capability, you can copy it |
249 |
|
to other tasks (if the server permits it, which is usually the case). |
250 |
|
In the Hurd, access to capabilities is always granted to a whole task, |
251 |
|
not to individual threads. |
252 |
|
|
253 |
This adds one addition IPC to each RPC. Furthermore, it makes |
\begin{comment} |
254 |
accounting the cost for managing capabilities difficult. It also |
There is no reason for the server not to permit it, because the |
255 |
keeps the IPC policy in system code which is imposed on the user. |
holder of the capability could also just act as a proxy for the |
256 |
|
intended receiver instead copying the capability to it. The |
257 |
|
operation might fail anyway, for example because of resource |
258 |
|
shortage, in particular if the server puts a quota on the number of |
259 |
|
capabilities a user can hold. |
260 |
|
\end{comment} |
261 |
|
|
262 |
The goal is to define and implement a capability system locally in |
Capabilities provide two essential services to the Hurd. They are |
263 |
each task, and without requiring mutual trust. |
used to restrict access to a server function, and they are the |
264 |
|
standard interface the components in the Hurd use to communicate with |
265 |
|
each others. Thus, it is important that their implementation is fast |
266 |
|
and secure. |
267 |
|
|
268 |
One difficulty is that in L4, IPC is always from thread to thread. |
\begin{comment} |
269 |
Thread identifiers are global and can be reused. So programs must be |
There are several ways to implement such a capability system. A |
270 |
careful not to send any sensitive data to the wrong thread. |
more traditional design would be a global, trusted capability server |
271 |
|
that provides capabilities to all its users. The L4 redirector |
272 |
|
could be used to reroute all client traffic automatically through |
273 |
|
this server. This approach has several disadvantages: |
274 |
|
|
275 |
|
\begin{itemize} |
276 |
|
\item It adds a lot of overhead to every single RPC, because all |
277 |
|
traffic has to be routed through the capability server, which must |
278 |
|
then perform the authentication on the server's behalf. |
279 |
|
\item It would be difficult to copy a capability to another task. |
280 |
|
Either the cap server would have to provide interfaces for clients |
281 |
|
to do it, or it would be have to know the message format for every |
282 |
|
interface and do it automatically. |
283 |
|
\item It would be a single point of failure. If it had a bug and |
284 |
|
crashed, the whole system would be affected. |
285 |
|
\item Users could not avoid it, it would be enforced system code. |
286 |
|
\item It is inflexible. It would be hard to replace or extend at |
287 |
|
run-time. |
288 |
|
\end{itemize} |
289 |
|
|
290 |
|
Another approach is taken by CORBA with IORs. IORs contain long |
291 |
|
random numbers which allow the server to identify a user of an |
292 |
|
object. This approach is not feasible for the following reasons: |
293 |
|
|
294 |
|
\begin{itemize} |
295 |
|
\item Even good random numbers can be guessed. Long enough random |
296 |
|
numbers can reduce the likelihood to arbitrary small numbers, |
297 |
|
though (below the probability of a hardware failure). |
298 |
|
\item Good random numbers are in short supply, and is slow to |
299 |
|
generate. Good pseudo random is faster, but it is still difficult |
300 |
|
to generate. The random number generator would become a critical |
301 |
|
part of the operating system. |
302 |
|
\item The random number had to be transfered in every single |
303 |
|
message. Because it would have to be long, it would have a |
304 |
|
significant negative impact on IPC performance. |
305 |
|
\end{itemize} |
306 |
|
\end{comment} |
307 |
|
|
308 |
\subsection{IPC Implementation Roadmap} |
The Hurd implements the capability system locally in each task. A |
309 |
|
common default implementation will be shared by all programs. |
310 |
|
However, a malicious untrusted program could do nothing to disturb the |
311 |
|
communication of other tasks. A capability will be identified in the |
312 |
|
client task by a the server thread and a local identifier (which can |
313 |
|
be different from client to client). The server thread will receive |
314 |
|
messages for the capabilities. The first argument in the message is |
315 |
|
the capability identifier. Although every task can get different IDs |
316 |
|
for the same capability, a well-behaving server will give the same ID |
317 |
|
to a client which already has a capability and gets the same |
318 |
|
capability from another client. So clients can compare capability IDs |
319 |
|
from the server numerically to check if two capabilities are the same, |
320 |
|
but only if one of the two IDs is received while the client already |
321 |
|
had the other one. |
322 |
|
|
323 |
|
Because access to a capability must be restricted, the server needs to |
324 |
|
be careful in only allowing registered and known users to access the |
325 |
|
capability. For this, the server must be sure that it can determine |
326 |
|
the sender of a message. In L4, this is easy on the surface: The |
327 |
|
kernel provides the receiving thread with the sender's thread ID, |
328 |
|
which also contains the task ID in the version field. However, the |
329 |
|
server must also know for sure if this task is the same task that it |
330 |
|
gave access to the capability. Comparing the task IDs numerically is |
331 |
|
not good enough, the server must also somehow have knowledge or |
332 |
|
influence on how task IDs are reused when tasks die and are created. |
333 |
|
|
334 |
|
The same is true for the client, of course, which trusts the server |
335 |
|
and thus must be sure that it is not tricked into trusting on |
336 |
|
unreliable data from an imposter, or sends sensitive data to it. |
337 |
|
|
338 |
\subsection{Threads and Tasks} |
\begin{comment} |
339 |
|
The task server wants to reuse thread numbers because that makes |
340 |
|
best use of kernel memory. Reusing task IDs, the version field of a |
341 |
|
thread ID, is not so important, but there are only 14 bits for the |
342 |
|
version field (and the lower six bits must not be all zero). So a |
343 |
|
thread ID is bound to be reused eventually. |
344 |
|
|
345 |
The Hurd will encode the task ID in the version part of the L4 thread |
Using the version field in a thread ID as a generation number is not |
346 |
ID. The version part can only be changed by the privileged system |
good enough, because it is so small. Even on 64-bit architectures, |
347 |
code, so it is protected by the kernel. This allows recipients of a |
where it is 32 bit long, it can eventually overflow. |
348 |
message to quickly determine the task from the sender's thread ID. |
\end{comment} |
349 |
|
|
350 |
Task IDs will not be reused as long as there are still tasks that |
The best way to prevent that a task can be tricked into talking to an |
351 |
might actively communicate with the (now destroyed) task. Task info |
imposter is to have the task server notify the task if the |
352 |
capabilities provided by the task server can be used for that. The |
communication partner dies. The task server must guarantee that the |
353 |
task info capability will also receive the task death notification (as |
task ID is not reused until all tasks that got such a notification |
354 |
a normap capability death notification). The task server will reuse a |
acknowledge that it is processed, and thus no danger of confusion |
355 |
task ID only when all task info capabilities for the task with that ID |
exists anymore. |
356 |
have been released. |
|
357 |
|
The task server will provide references to task IDs in form of |
358 |
|
\emph{task info capabilities}. If a task has a task info capability |
359 |
|
for another task, it will prevent that this other task's task ID is |
360 |
|
reused even if that task dies, and it will also make sure that task |
361 |
|
death notifications are delivered in that case. |
362 |
|
|
363 |
This of course can open a DoS attack. Programs can attempt to acquire |
\begin{comment} |
364 |
task info capabilities and never release them. Several strategies can |
Because only the task server can create and destroy tasks, and |
365 |
be applied to compensate that: The task server can automatically time |
assign task IDs, there is no need to hold such task info |
366 |
out task info capability references to dead tasks. The proc server |
capabilities for the task server, nor does the task server need to |
367 |
can show dead task IDs with task info capability references as some |
hold task info capabilities for its clients. This avoids the |
368 |
variant of zombie tasks, and provide a way to list all tasks |
obvious bootstrap problem in providing capabilities in the task |
369 |
preventing the task ID from being reused, allowing the system |
server. This will even work if the task server is not the real task |
370 |
administrator to identify malicious or faulty users. Task ID |
server, but a proxy task server (see section \ref{proxytaskserver} |
371 |
references can be taken into account in quota restrictions, to |
on page \pageref{proxytaskserver}). |
372 |
encourage a user to release them when they are not needed anymore (in |
\end{comment} |
373 |
particular, a user holding a task ID reference to a dead task could be |
|
374 |
punished with the same costs as for an additional normal task owned by |
As task IDs are a global resource, care has to be taken that this |
375 |
the user). Another idea is to not allow any task to allocate more |
approach does not allow for a DoS-attack by exhausting the task ID |
376 |
task info capabilities than there are live tasks in the system, plus |
number space. |
377 |
some slack. This provides a high incentive for tasks to release their |
|
378 |
info caps (and if they get an error, they could block until their |
\begin{comment} |
379 |
notification system has processed the task death notification and |
Several strategies can be taken: |
380 |
released the reference, and try again). |
|
381 |
|
\begin{itemize} |
382 |
|
\item Task death notifications can be monitored. If there is no |
383 |
|
acknowdgement within a certain time period, the task server could |
384 |
|
be allowed to reuse the task ID anyway. This is not a good |
385 |
|
strategy because it can considerably weaken the security of the |
386 |
|
system (capabilities might be leaked to tasks which reuse such a |
387 |
|
task ID reclaimed by force). |
388 |
|
\item The proc server can show dead task IDs which are not released |
389 |
|
yet, in analogy to the zombie processes in Unix. It can also make |
390 |
|
available the list of tasks which prevent reusing the task ID, to |
391 |
|
allow users or the system administrator to clean up manually. |
392 |
|
\item Quotas can be used to punish users which do not acknowledge |
393 |
|
task death timely. For example, if the number of tasks the user |
394 |
|
is allowed to create is restricted, the task info caps that the |
395 |
|
user holds for dead tasks could be counted toward that limit. |
396 |
|
\item Any task could be restricted to as many task ID references as |
397 |
|
there are live tasks in the system, plus some slack. That would |
398 |
|
prevent the task from creating new task info caps if it does not |
399 |
|
release old ones from death tasks. The slack would be provided to |
400 |
|
not unnecessarily slow down a task that processes task death |
401 |
|
notifications asynchronously to making connections with new tasks. |
402 |
|
\end{itemize} |
403 |
|
|
404 |
|
In particular the last two approaches should proof to be effective |
405 |
|
in providing an incentive for tasks to release task info caps they |
406 |
|
do not need anymore. |
407 |
|
\end{comment} |
408 |
|
|
409 |
|
|
410 |
|
\subsection{Capabilities} |
411 |
|
|
412 |
|
This subsection contains implementation details about capabilities. |
413 |
|
|
414 |
|
A server will usually operate on objects, and not capabilities. In |
415 |
|
the case of a filesystem, this could be file objects, for example. |
416 |
|
|
417 |
|
\begin{comment} |
418 |
|
In the Hurd, filesystem servers have to keep different objects for |
419 |
|
each time a file is looked up (or ``opened''), because some state, |
420 |
|
for example authentication, open flags and record locks, are |
421 |
|
associated not with the file directly, but with this instance of |
422 |
|
opening the file. Such a state structure (``credential'') will also |
423 |
|
contain a pointer and reference to the actual file node. For |
424 |
|
simplicity, we will assume that the capability is associated with a |
425 |
|
file node directly. |
426 |
|
\end{comment} |
427 |
|
|
428 |
|
To provide access to the object to another task, the server creates a |
429 |
|
capability, and associates it with the object (by setting a hook |
430 |
|
variable in the capability). From this capability, the server can |
431 |
|
either create send references to itself, or to other tasks. If the |
432 |
|
server creates send references for itself, it can use the capability |
433 |
|
just as it can use capabilities implemented by other servers. This |
434 |
|
makes access to locally and remotely implemented capabilities |
435 |
|
identical. If you write code to work on capabilities, it can be used |
436 |
|
for remote objects as well as for local objects. |
437 |
|
|
438 |
|
If the server creates a send reference for another task (a client), a |
439 |
|
new capability ID will be created for this task. This ID will only be |
440 |
|
valid for this task, and should be returned to the client. |
441 |
|
|
442 |
|
The client itself will create a capability object from this capability |
443 |
|
ID. The capability will also contain information about the server, |
444 |
|
for example the server thread which should be used for sending |
445 |
|
messages to the capability. |
446 |
|
|
447 |
|
If the client wants to send a message, it will send it to the provided |
448 |
|
server thread, and use the capability ID it got from the server as the |
449 |
|
first argument in the RPC. The server receives the message, and now |
450 |
|
has to look up the capability ID in the list of capabilties for this |
451 |
|
task. |
452 |
|
|
453 |
|
\begin{comment} |
454 |
|
The server knows the task ID from the version field of the sender's |
455 |
|
thread ID. It can look up the list of capabilities for this task in |
456 |
|
a hash table. The capability ID can be an index into an array, so |
457 |
|
the server only needs to perform a range check. This allows to |
458 |
|
verify quickly that the user is allowed to access the object. |
459 |
|
|
460 |
|
This is not enough if several systems run in parallel on the same |
461 |
|
host. Then the version ID for the threads in the other systems will |
462 |
|
not be under the control of the Hurd's task server, and can thus not |
463 |
|
be trusted. The server can still use the version field to find out |
464 |
|
the task ID, which will be correct \emph{if the thread is part of |
465 |
|
the same subsystem}. It also has to verify that the thread |
466 |
|
belongs to this subsystem. Hopefully the subsystem will be encoded |
467 |
|
in the thread ID. Otherwise, the task server has to be consulted |
468 |
|
(and, assuming that thread numbers are not shared by the different |
469 |
|
systems, the result can be cached). |
470 |
|
\end{comment} |
471 |
|
|
472 |
|
The server reads out the capability associated with the capability ID, |
473 |
|
and invokes the server stub according to the message ID field in the |
474 |
|
message. |
475 |
|
|
476 |
|
After the message is processed, the server sends it reply to the |
477 |
|
sender thread with a zero timeout. |
478 |
|
|
479 |
|
\begin{comment} |
480 |
|
Servers must never block on sending messages to clients. Even a |
481 |
|
small timeout can be used for DoS-attacks. The client can always |
482 |
|
make sure that it receives the reply by using a combined send and |
483 |
|
receive operation together with an infinite timeout. |
484 |
|
\end{comment} |
485 |
|
|
486 |
|
The above scheme assumes that the server and the client already have |
487 |
|
task info caps for the respective other task. This is the normal |
488 |
|
case, because acquiring these task info caps is part of the protocol |
489 |
|
that is used when a capability is copied from one task to another. |
490 |
|
|
491 |
|
|
492 |
|
\subsubsection{Bootstrapping a client-server connection} |
493 |
|
|
494 |
|
If the client and the server do not know about each other yet, then |
495 |
|
they can bootstrap a connection without support from any other task |
496 |
|
except the task server. The purpose of the initial handshake is to |
497 |
|
give both participants a chance to acquire a task info cap for the |
498 |
|
other participants task ID, so they can be sure that from there on |
499 |
|
they will always talk to the same task as they talked to before. |
500 |
|
|
501 |
|
\paragraph{Preconditions} |
502 |
|
The client knows the thread ID of the server thread that receives and |
503 |
|
processes the bootstrap messages. Some other task might hold a task |
504 |
|
info capability to the server the client wants to connect to. |
505 |
|
|
506 |
|
\begin{comment} |
507 |
|
If no such other tasks exists, the protocol will still work. |
508 |
|
However, the client might not get a connection to the server that |
509 |
|
run at the time the client started the protocol, but rather to the |
510 |
|
server that run at the time the client acquired the task info cap |
511 |
|
for the server's task ID (after step 1 below). |
512 |
|
|
513 |
|
This is similar to how sending signals works in Unix: Technically, |
514 |
|
at the time you write \texttt{kill 203}, and press enter, you do not |
515 |
|
know if the process with the PID 203 you thought of will receive the |
516 |
|
signal, or some other process that got the PID in the time between |
517 |
|
you getting the information about the PID and writing the |
518 |
|
\texttt{kill}-command. |
519 |
|
\end{comment} |
520 |
|
|
521 |
|
FIXME: Here should be the pseudo code for the protocol. For now, you |
522 |
|
have to take it out of the long version. |
523 |
|
|
524 |
|
\begin{enumerate} |
525 |
|
|
526 |
|
\item The client acquires a task info capability for the server's task |
527 |
|
ID, either directly from the task server, or from another task in a |
528 |
|
capability copy. From that point on, the client can be sure to |
529 |
|
always talk to the same task when talking to the server. |
530 |
|
|
531 |
|
Of course, if the client already has a task info cap for the server |
532 |
|
it does not need to do anything in this step. |
533 |
|
|
534 |
|
\begin{comment} |
535 |
|
As explained above, if the client does not have any other task |
536 |
|
holding the task info cap already, it has no secure information |
537 |
|
about what this task is for which it got a task info cap. |
538 |
|
\end{comment} |
539 |
|
|
540 |
|
\item The client sends a message to the server, requesting the initial |
541 |
|
handshake. |
542 |
|
|
543 |
|
\item The server receives the message, and acquires a task info cap |
544 |
|
for the client task (directly from the task server). |
545 |
|
|
546 |
|
Of course, if the server already has a task info cap for the client |
547 |
|
it does not need to do anything in this step. |
548 |
|
|
549 |
|
\begin{comment} |
550 |
|
At this point, the server knows that future messages from this task |
551 |
|
will come from the same task as it got the task info cap for. |
552 |
|
However, it does not know that this is the same task that sent the |
553 |
|
initial handshake request in step 2 above. This shows that there is |
554 |
|
no sense in verifying the task ID or perform any other |
555 |
|
authentication before acquiring the task info cap. |
556 |
|
\end{comment} |
557 |
|
|
558 |
|
\item The server replies to the initial handshake request with an |
559 |
|
empty reply message. |
560 |
|
|
561 |
|
\begin{comment} |
562 |
|
Because the reply now can go to a different task than the request |
563 |
|
came from, sending the reply might fail. It might also succeed and |
564 |
|
be accepted by the task that replaced the requestor. Or it might |
565 |
|
succeed normally. The important thing is that it does not matter to |
566 |
|
the server at all. It would have provided the same ``service'' to |
567 |
|
the ``imposter'' of the client, if he had bothered to do the |
568 |
|
request. As no authentication is done yet, there is no point for |
569 |
|
the server to bother. |
570 |
|
|
571 |
|
This means however, that the server needs to be careful in not |
572 |
|
consuming too many resources for this service. However, this is |
573 |
|
easy to achieve. Only one task info cap per client task will ever |
574 |
|
be held in the server. The server can either keep it around until |
575 |
|
the task dies (and a task death notification is received), or it can |
576 |
|
clean it up after some timeout if the client does not follow up and |
577 |
|
do some real authentication. |
578 |
|
\end{comment} |
579 |
|
|
580 |
|
\item The client receives the reply message to its initial handshake |
581 |
|
request. |
582 |
|
|
583 |
|
\item The client sends a request to create its initial capability. |
584 |
|
How this request looks depends on the type of the server and the |
585 |
|
initial capabilities it provides. Here are some examples: |
586 |
|
|
587 |
|
\begin{itemize} |
588 |
|
\item A filesystem might provide an unauthenticated root directory |
589 |
|
object in return of the underlying node capability, which is |
590 |
|
provided by the parent filesystem and proves to the filesystem |
591 |
|
that the user was allowed to look up the root node of this |
592 |
|
filesystem (see section \ref{xfslookup} on page |
593 |
|
\pageref{xfslookup}). |
594 |
|
|
595 |
|
\begin{comment} |
596 |
|
In this example, the parent filesystem will either provide the |
597 |
|
task info cap for the child filesystem to the user, or it will |
598 |
|
hold the task info cap while the user is creating their own |
599 |
|
(which the user has to verify by repeating the lookup, though). |
600 |
|
Again, see section \ref{xfslookup} on page \pageref{xfslookup}. |
601 |
|
|
602 |
|
The unauthenticated root directory object will then have the be |
603 |
|
authenticated using the normal reauthentication mechanism (see |
604 |
|
section \ref{auth} on pageref{auth}). This can also be combined |
605 |
|
in a single RPC. |
606 |
|
\end{comment} |
607 |
|
|
608 |
|
\item Every process acts as a server that implements the signal |
609 |
|
capability for this process. Tasks who want to send a signal to |
610 |
|
another task can perform the above handshake, and then provide |
611 |
|
some type of authentication capability that indicates that they |
612 |
|
are allowed to send a signal. Different authentication |
613 |
|
capabilities can be accepted by the signalled task for different |
614 |
|
types of signals. |
615 |
|
|
616 |
|
\begin{comment} |
617 |
|
The Hurd used to store the signal capability in the proc server, |
618 |
|
where authorized tasks could look it up. This is no longer |
619 |
|
possible because a server can not accept capabilities |
620 |
|
implemented by untrusted tasks, see below. |
621 |
|
\end{comment} |
622 |
|
\end{itemize} |
623 |
|
|
624 |
|
\item The server replies with whatever capability the client |
625 |
|
requested, provided that the client could provide the necessary |
626 |
|
authentication capabilities, if any. |
627 |
|
|
628 |
|
\begin{comment} |
629 |
|
It is not required that the server performs any authentication at |
630 |
|
all, but it is recommended, and all Hurd servers will do so. |
631 |
|
|
632 |
|
In particular, the server should normally only allow access from |
633 |
|
tasks running in the same system, if running multiple systems on |
634 |
|
the same host is possible. |
635 |
|
\end{comment} |
636 |
|
\end{enumerate} |
637 |
|
|
638 |
|
\paragraph{Result} |
639 |
|
The client has a task info capability for the server and an |
640 |
|
authenticated capability. The server has a task info capability for |
641 |
|
the client and seen some sort of authentication for the capability it |
642 |
|
gave to the client. |
643 |
|
|
644 |
|
\begin{comment} |
645 |
|
If you think that the above protocol is complex, you have seen |
646 |
|
nothing yet! Read on. |
647 |
|
\end{comment} |
648 |
|
|
649 |
|
|
650 |
|
\subsubsection{Returning a capability from a server to a client} |
651 |
|
|
652 |
|
Before we go on to the more complex case of copying a capability from |
653 |
|
one client to another, let us point out that once a client has a |
654 |
|
capability from a server, it is easy for the server to return more |
655 |
|
capabilities it implements to the client. |
656 |
|
|
657 |
|
The server just needs to create the capability, acquire a capability |
658 |
|
ID in the client's cap ID space, and return the information in the |
659 |
|
reply RPC. |
660 |
|
|
661 |
|
FIXME: Here should be the pseudo code for the protocol. For now, you |
662 |
|
have to take it out of the long version. |
663 |
|
|
664 |
|
\begin{comment} |
665 |
|
The main point of this section is to point out that only one task |
666 |
|
info capability is required to protect all capabilities provided to |
667 |
|
a single task. The protocols described here always assume that no |
668 |
|
task info caps are held by anyone (except those mentioned in the |
669 |
|
preconditions). In reality, sometimes the required task info caps |
670 |
|
will already be held. |
671 |
|
\end{comment} |
672 |
|
|
673 |
|
|
674 |
|
\subsubsection{Copying a capability from one client to another task} |
675 |
|
|
676 |
|
The most complex operation in managing capabilities is to copy or move |
677 |
|
a capability from the client to another task, which subsequently |
678 |
|
becomes a client of the server providing the capability. The |
679 |
|
difficulty here lies in the fact that the protocol should be fast, but |
680 |
|
also robust and secure. If any of the participants dies unexpectedly, |
681 |
|
or any of the untrusted participants is malicious, the others should |
682 |
|
not be harmed. |
683 |
|
|
684 |
|
\paragraph{Preconditions} |
685 |
|
The client $C$ has a capability from server $S$ (this implies that $C$ |
686 |
|
has a task info cap for $S$ and $S$ has a task info cap for $C$). It |
687 |
|
wants to copy the capability to the destination task $D$. For this, |
688 |
|
it will have to make RPCs to $D$, so $C$ has also a capability from |
689 |
|
$D$ (this implies that $C$ has a task info cap for $D$ and $D$ has a |
690 |
|
task info cap for $C$). Of course, the client $C$ trusts its servers |
691 |
|
$S$ and $D$. $D$ might trust $S$ or not, and thus accept or reject |
692 |
|
the capability that $C$ wants to give to $D$. $S$ does not trust |
693 |
|
either $C$ or $D$. |
694 |
|
|
695 |
|
The task server is also involved, because it provides the task info |
696 |
|
capabilities. Everyone trusts the task server they use. This does |
697 |
|
not need to be the same one for every participant. |
698 |
|
|
699 |
|
FIXME: Here should be the pseudo code for the protocol. For now, you |
700 |
|
have to take it out of the long version. |
701 |
|
|
702 |
|
\begin{enumerate} |
703 |
|
\item The client invokes the \verb/cap_ref_cont_create/ RPC on the |
704 |
|
capability, providing the task ID of the intended receiver $D$ of |
705 |
|
the capability. |
706 |
|
|
707 |
|
\item The server receives the \verb/cap_ref_cont_create/ RPC from the |
708 |
|
client. It requests a task info cap for $D$ from its trusted task |
709 |
|
server, under the constraint that $C$ is still living. |
710 |
|
|
711 |
|
\begin{comment} |
712 |
|
A task can provide a constraint when creating a task info cap in |
713 |
|
the task server. The constraint is a task ID. The task server |
714 |
|
will only create the task info cap and return it if the task with |
715 |
|
the constraint task ID is not destroyed. This allows for a task |
716 |
|
requesting a task info capability to make sure that another task, |
717 |
|
which also holds this task info cap, is not destroyed. This is |
718 |
|
important, because if a task is destroyed, all the task info caps |
719 |
|
it held are released. |
720 |
|
|
721 |
|
In this case, the server relies on the client to hold a task info |
722 |
|
cap for $D$ until it established its own. See below for what can |
723 |
|
go wrong if the server would not provide a constraint and both, |
724 |
|
the client and the destination task would die unexpectedly. |
725 |
|
\end{comment} |
726 |
|
|
727 |
|
Now that the server established its own task info cap for $D$, it |
728 |
|
creates a reference container for $D$, that has the following |
729 |
|
properties: |
730 |
|
|
731 |
|
\begin{itemize} |
732 |
|
\item The reference container has a single new reference for the |
733 |
|
capability. |
734 |
|
|
735 |
|
\item The reference container has an ID that is unique among all |
736 |
|
reference container IDs for the client $C$. |
737 |
|
|
738 |
|
\item The reference container is associated with the client $C$. If |
739 |
|
$C$ dies, and the server processes the task death notification for |
740 |
|
it, the server will destroy the reference container and release |
741 |
|
the capability reference it has (if any). All resources |
742 |
|
associated with the reference container will be released. If this |
743 |
|
reference container was the only reason for $S$ to hold the task |
744 |
|
info cap for $D$, the server will also release the task info cap |
745 |
|
for $D$. |
746 |
|
|
747 |
|
\item The reference container is also associated with the |
748 |
|
destination task $D$. If $D$ dies, and the server processes the |
749 |
|
task death notification for it, the server will release the |
750 |
|
capability reference that is in the reference container (if any). |
751 |
|
It will not destroy the part of the container that is associated |
752 |
|
with $C$. |
753 |
|
\end{itemize} |
754 |
|
|
755 |
|
The server returns the reference container ID $R$ to the client. |
756 |
|
|
757 |
|
\item The client receives the reference container ID $R$. |
758 |
|
|
759 |
|
\begin{comment} |
760 |
|
If several capabilities have to be copied in one message, the |
761 |
|
above steps need to be repeated for each capability. With |
762 |
|
appropriate interfaces, capabilities could be collected so that |
763 |
|
only one call per server has to be made. We are assuming here |
764 |
|
that only one capability is copied. |
765 |
|
\end{comment} |
766 |
|
|
767 |
|
\item The client sends the server thread ID $T$ and the reference |
768 |
|
container ID $R$ to the destination task $D$. |
769 |
|
|
770 |
|
\item The destination task $D$ receives the server thread ID $T$ and |
771 |
|
the reference container ID $R$ from $C$. |
772 |
|
|
773 |
|
It now inspects the server thread ID $T$, and in particular the task |
774 |
|
ID component of it. $D$ has to make the decision if it trusts this |
775 |
|
task to be a server for it, or if it does not trust this task. |
776 |
|
|
777 |
|
If $D$ trusts $C$, it might decide to always trust $T$, too, |
778 |
|
irregardless of what task contains $T$. |
779 |
|
|
780 |
|
If $D$ does not trust $C$, it might be more picky about the task |
781 |
|
that contains $T$. This is because $D$ will have to become a client |
782 |
|
of $T$, so it will trust it. For example, it will block on messages |
783 |
|
it sends to $T$. |
784 |
|
|
785 |
|
\begin{comment} |
786 |
|
If $D$ is a server, it will usually only accept capabilities from |
787 |
|
its client that are provided by specific other servers it trusts. |
788 |
|
This can be the authentication server, for example (see section |
789 |
|
\ref{auth} on page \pageref{auth}). |
790 |
|
|
791 |
|
Usually, the type of capability that $D$ wants to accept from $C$ |
792 |
|
is then further restricted, and only one possible trusted server |
793 |
|
implements that type of capabilities. Thus, $D$ can simply |
794 |
|
compare the task ID of $T$ with the task ID of its trusted server |
795 |
|
(authentication server, ...) to make the decision if it wants to |
796 |
|
accept the capability or not. |
797 |
|
\end{comment} |
798 |
|
|
799 |
|
If $D$ does not trust $T$, it replies to $C$ (probably with an error |
800 |
|
value indicating why the capability was not accepted). In that |
801 |
|
case, jump to step 8. |
802 |
|
|
803 |
|
Otherwise, it requests a task info cap for $S$ from its trusted task |
804 |
|
server, under the constraint that $C$ is still living. |
805 |
|
|
806 |
|
Then $D$ sends a \verb/cap_ref_cont_accept/ RPC to the server $S$, |
807 |
|
providing the task ID of the client $C$ and the reference container |
808 |
|
ID $R$. |
809 |
|
|
810 |
|
\begin{comment} |
811 |
|
\verb/cap_ref_cont_accept/ is one of the few interfaces that is not |
812 |
|
sent to a (real) capability, of course. Nevertheless, it is part of |
813 |
|
the capability object interface, hence the name. You can think of |
814 |
|
it as a static member in the capability class, that does not require |
815 |
|
an instance of the class. |
816 |
|
\end{comment} |
817 |
|
|
818 |
|
\item The server receives the \verb/cap_ref_cont_accept/ RPC from the |
819 |
|
destination task $D$. It verifies that a reference container exists |
820 |
|
with the ID $R$, that is associated with $D$ and $C$. |
821 |
|
|
822 |
|
\begin{comment} |
823 |
|
The server will store the reference container in data structures |
824 |
|
associated with $C$, under an ID that is unique but local to $C$. |
825 |
|
So $D$ needs to provide both information, the task ID and the |
826 |
|
reference container ID of $C$. |
827 |
|
\end{comment} |
828 |
|
|
829 |
|
If that is the case, it takes the reference from the reference |
830 |
|
container, and creates a capability ID for $D$ from it. The |
831 |
|
capability ID for $D$ is returned in the reply message. |
832 |
|
|
833 |
|
From that moment on, the reference container is deassociated from |
834 |
|
$D$. It is still associated with $C$, but it does not contain any |
835 |
|
reference for the capability. |
836 |
|
|
837 |
|
\begin{comment} |
838 |
|
It is not deassociated from $C$ and removed completely, so that |
839 |
|
its ID $R$ (or at least the part of it that is used for $C$) is |
840 |
|
not reused. $C$ must explicitely destroy the reference container |
841 |
|
anyway because $D$ might die unexpectedly or return an error that |
842 |
|
gives no indication if it accepted the reference or not. |
843 |
|
\end{comment} |
844 |
|
|
845 |
|
\item The destination task $D$ receives the capability ID and enters |
846 |
|
it into its capability system. It sends a reply message to $C$. |
847 |
|
|
848 |
|
\begin{comment} |
849 |
|
If the only purpose of the RPC was to copy the capability, the |
850 |
|
reply message can be empty. Usually, capabilities will be |
851 |
|
transfered as part of a larger operation, though, and more work |
852 |
|
will be done by $D$ before returning to $C$. |
853 |
|
\end{comment} |
854 |
|
|
855 |
|
\item The client $C$ receives the reply from $D$. Irregardless if it |
856 |
|
indicated failure or success, it will now send the |
857 |
|
\verb/cap_ref_cont_destroy/ message to the server $S$, providing the |
858 |
|
reference container $R$. |
859 |
|
|
860 |
|
\begin{comment} |
861 |
|
This message can be a simple message. It does not require a reply |
862 |
|
from the server. |
863 |
|
\end{comment} |
864 |
|
|
865 |
|
\item The server receives the \verb/cap_ref_cont_destroy/ message and |
866 |
|
removes the reference container $R$. The reference container is |
867 |
|
deassociated from $C$ and $D$. If this was the only reason that $S$ |
868 |
|
held a task info cap for $D$, this task info cap is also released. |
869 |
|
|
870 |
|
\begin{comment} |
871 |
|
Because the reference container can not be deassociated from $C$ |
872 |
|
by any other means than this interface, the client does not need |
873 |
|
to provide $D$. $R$ can not be reused without the client $C$ |
874 |
|
having it destroyed first. This is different from the |
875 |
|
\verb/cap_ref_cont_accept/ call made by $D$, see above. |
876 |
|
\end{comment} |
877 |
|
|
878 |
|
\end{enumerate} |
879 |
|
|
880 |
|
\paragraph{Result} |
881 |
|
For the client $C$, nothing has changed. The destination task $D$ |
882 |
|
either did not accept the capability, and nothing has changed for it, |
883 |
|
and also not for the server $S$. Or $D$ accepted the capability, and |
884 |
|
it now has a task info cap for $S$ and a reference to the capability |
885 |
|
provided by $S$. In this case, the server $S$ has a task info cap for |
886 |
|
$D$ and provides a capability ID for this task. |
887 |
|
|
888 |
|
The above protocol is for copying a capability from $C$ to $D$. If |
889 |
|
the goal was to move the capability, then $C$ can now release its |
890 |
|
reference to it. |
891 |
|
|
892 |
|
\begin{comment} |
893 |
|
Originally we considered to move capabilities by default, and |
894 |
|
require the client to acquire an additional reference if it wanted |
895 |
|
to copy it instead. However, it turned out that for the |
896 |
|
implementation, copying is easier to handle. One reason is that the |
897 |
|
client usually will use local reference counting for the |
898 |
|
capabilities it holds, and with local reference counting, one |
899 |
|
server-side reference is shared by many local references. In that |
900 |
|
case, you would need to acquire a new server-side reference even if |
901 |
|
you want to move the capability. The other reason is cancellation. |
902 |
|
If an RPC is cancelled, and you want to back out of it, you need to |
903 |
|
restore the original situation. And that is easier if you do not |
904 |
|
change the original situation in the first place until the natural |
905 |
|
``point of no return''. |
906 |
|
\end{comment} |
907 |
|
|
908 |
|
The above protocol quite obviously achieves the result as described in |
909 |
|
the above concluding paragraph. However, many other, and often |
910 |
|
simpler, protocols would also do that. The other protocols we looked |
911 |
|
at are not secure or robust though, or require more operations. To |
912 |
|
date we think that the above is the shortest (in particular in number |
913 |
|
of IPC operations) protocol that is also secure and robust (and if it |
914 |
|
is not we think it can be fixed to be secure and robust with minimal |
915 |
|
changes). We have no proof for its correctness. Our confidence comes |
916 |
|
from the scrutiny we applied to it. If you find a problem with the |
917 |
|
above protocol, or if you can prove various aspects of it, we would |
918 |
|
like to hear about it. |
919 |
|
|
920 |
|
To understand why the protocol is laid out as it is, and why it is a |
921 |
|
secure and robust protocol, one has to understand what could possibly |
922 |
|
go wrong and why it does not cause any problems for any participant if |
923 |
|
it follows its part of the protocol (independent on what the other |
924 |
|
participants do). In the following paragraphs, various scenarios are |
925 |
|
suggested where things do not go as expected in the above protocol. |
926 |
|
This is probably not a complete list, but it should come close to it. |
927 |
|
If you find any other problematic scenario, again, let us know. |
928 |
|
|
929 |
|
\begin{comment} |
930 |
|
Although some comments like this appear in the protocol description |
931 |
|
above, many comments have been spared for the following analysis of |
932 |
|
potential problems. Read the analysis carefully, as it provides |
933 |
|
important information about how, and more importantly, why it works. |
934 |
|
\end{comment} |
935 |
|
|
936 |
|
\paragraph{The server $S$ dies} |
937 |
|
What happens if the server S dies unexpectedly sometime throughout the |
938 |
|
protocol? |
939 |
|
|
940 |
|
\begin{comment} |
941 |
|
At any time a task dies, the task info caps it held are released. |
942 |
|
Also, task death notifications are sent to any task that holds task |
943 |
|
info caps to the now dead task. The task death notifications will |
944 |
|
be processed asynchrnouly, so they might be processed immediately, |
945 |
|
or at any later time, even much later after the task died! So one |
946 |
|
important thing to keep in mind is that the release of task info |
947 |
|
caps a task held, and other tasks noticing the task death, are |
948 |
|
always some time apart. |
949 |
|
\end{comment} |
950 |
|
|
951 |
|
Because the client $C$ holds a task info cap for $S$ no imposter can |
952 |
|
get the task ID of $S$. $C$ and $D$ will get errors when trying to |
953 |
|
send messages to $S$. |
954 |
|
|
955 |
|
\begin{comment} |
956 |
|
You might now wonder what happens if $C$ also dies, or if $C$ is |
957 |
|
malicious and does not hold the task info cap. You can use this as |
958 |
|
an exercise, and try to find the answer on your own. The answers |
959 |
|
are below. |
960 |
|
\end{comment} |
961 |
|
|
962 |
|
Eventually, $C$ (and $D$ if it already got the task info cap for $S$) |
963 |
|
will process the task death notification and clean up their state. |
964 |
|
|
965 |
|
\paragraph{The client $C$ dies} |
966 |
|
The server $S$ and the destination task $D$ hold a task info cap for |
967 |
|
$C$, so no imposter can get its task ID. $S$ and $D$ will get errors |
968 |
|
when trying to send messages to $C$. Depending on when $C$ dies, the |
969 |
|
capability might be copied successfully or not at all. |
970 |
|
|
971 |
|
Eventually, $S$ and $D$ will process the task death notification and |
972 |
|
release all resources associated with $C$. If the reference was not |
973 |
|
yet copied, this will include the reference container associated with |
974 |
|
$C$, if any. If the reference was already copied, this will only |
975 |
|
include the empty reference container, if any. |
976 |
|
|
977 |
|
\begin{comment} |
978 |
|
Of course, the participants need to use internal locking to protect |
979 |
|
the integrity of their internal data structures. The above protocol |
980 |
|
does not show where locks are required. In the few cases where some |
981 |
|
actions must be performed atomically, a wording is used that |
982 |
|
suggests that. |
983 |
|
\end{comment} |
984 |
|
|
985 |
|
\paragraph{The destination task $D$ dies} |
986 |
|
|
987 |
|
The client $C$ holds a task info cap for $D$ over the whole operation, |
988 |
|
so no imposter can get its task ID. Depending on when $D$ dies, it |
989 |
|
has either not yet accepted the capability, then $C$ will clean up by |
990 |
|
destroying the reference container, or it has, and then $S$ will clean |
991 |
|
up its state when it processes the task death notification for $D$. |
992 |
|
|
993 |
|
\paragraph{The client $C$ and the destination task $D$ die} |
994 |
|
|
995 |
|
This scenario is the reason why the server acquires its own task info |
996 |
|
cap for $D$ so early, and why it must do that under the constraint |
997 |
|
that $C$ still lives. If $C$ and $D$ die before the server created |
998 |
|
the reference container, then either no request was made, or creating |
999 |
|
the task info cap for $D$ fails because of the constraint. If $C$ and |
1000 |
|
$D$ die afterwards, then no imposter can get the task ID of $D$ and |
1001 |
|
try to get at the reference in the container, because the server has |
1002 |
|
its own task info cap for $D$. |
1003 |
|
|
1004 |
|
\begin{comment} |
1005 |
|
This problem was identified very late in the development of this |
1006 |
|
protocol. We just did not think of both clients dieing at the same |
1007 |
|
time! In an earlier version of the protocol, the server would |
1008 |
|
acquire its task info cap when $D$ accepts its reference. This is |
1009 |
|
too late: If $C$ and $D$ die just before that, an imposter with |
1010 |
|
$D$'s task ID can try to get the reference in the container before |
1011 |
|
the server processes the task death notification for $C$ and |
1012 |
|
destroys it. |
1013 |
|
\end{comment} |
1014 |
|
|
1015 |
|
Eventually, the server will receive and process the task death |
1016 |
|
notifications. If it processes the task death notification for $C$ |
1017 |
|
first, it will destroy the whole container immediately, including the |
1018 |
|
reference, if any. If it processes the task death notification for |
1019 |
|
$D$ first, it will destroy the reference, and leave behind the empty |
1020 |
|
container associated with $C$, until the other task death notification |
1021 |
|
is processed. Either way no imposter can get at the capability. |
1022 |
|
|
1023 |
|
Of course, if the capability was already copied at the time $C$ and |
1024 |
|
$D$ die, the server will just do the normal cleanup. |
1025 |
|
|
1026 |
|
\paragraph{The client $C$ and the server $S$ die} |
1027 |
|
|
1028 |
|
This scenario does not cause any problems, because on the one hand, |
1029 |
|
the destination task $D$ holds a task info cap for $C$, and it |
1030 |
|
acquires its own task info cap for $S$. Although it does this quite |
1031 |
|
late in the protocol, it does so under the constraint that $C$ still |
1032 |
|
lives, which has a task info cap for $S$ for the whole time (until it |
1033 |
|
dies). It also gets the task info cap for $S$ before sending any |
1034 |
|
message to it. An imposter with the task ID of $S$, which it was |
1035 |
|
possible to get because $C$ died early, would not receive any message |
1036 |
|
from $D$ because $D$ uses $C$ as its constraint in acquireing the task |
1037 |
|
info cap for $S$. |
1038 |
|
|
1039 |
|
\paragraph{The destination task $D$ and the server $S$ die} |
1040 |
|
|
1041 |
|
As $C$ holds task info caps for $S$ and $D$, there is nothing that can |
1042 |
|
go wrong here. Eventually, the task death notifications are |
1043 |
|
processed, but the task info caps are not released until the protocol |
1044 |
|
is completed or aborted because of errors. |
1045 |
|
|
1046 |
|
\paragraph{The client $C$, the destination task $D$ and the server $S$ die} |
1047 |
|
|
1048 |
|
Before the last one of these dies, you are in one of the scenarios |
1049 |
|
which already have been covered. After the last one dies, there is |
1050 |
|
nothing to take care of anymore. |
1051 |
|
|
1052 |
|
\begin{comment} |
1053 |
|
In this case your problem is probably not the capability copy |
1054 |
|
protocol, but the stability of your software! Go fix some bugs. |
1055 |
|
\end{comment} |
1056 |
|
|
1057 |
|
So far the scenarios where one or more of the participating tasks die |
1058 |
|
unexpectedly. They could also die purposefully. Other things that |
1059 |
|
tasks can try to do purposefully to break the protocol are presented |
1060 |
|
in the following paragraphs. |
1061 |
|
|
1062 |
|
\begin{comment} |
1063 |
|
A task that tries to harm other tasks by not following a protocol |
1064 |
|
and behaving as other tasks might expect it is malicious. Beside |
1065 |
|
security concerns, this is also an issue of robustness, because |
1066 |
|
malicious behaviour can also be triggered by bugs rather than bad |
1067 |
|
intentions. |
1068 |
|
|
1069 |
|
It is difficult to protect against malicious behaviour by trusted |
1070 |
|
components, like the server $S$, which is trusted by both $C$ and |
1071 |
|
$D$. If a trusted component is compromised or buggy, ill |
1072 |
|
consequences for software that trusts it must be expected. Thus, no |
1073 |
|
analysis is provided for scenarious involving a malicious or buggy |
1074 |
|
server $S$. |
1075 |
|
\end{comment} |
1076 |
|
|
1077 |
|
\paragraph{The client $C$ is malicious} |
1078 |
|
|
1079 |
|
If the client $C$ wants to break the protocol, it has numerous |
1080 |
|
possibilities to do so. The first thing it can do is to provide a |
1081 |
|
wrong destination task ID when creating the container. But in this |
1082 |
|
case, the server will return an error to $D$ when it tries to accept |
1083 |
|
it, and this will give $D$ a chance to notice the problem and clean |
1084 |
|
up. This also would allow for some other task to receive the |
1085 |
|
container, but the client can give the capability to any other task it |
1086 |
|
wants to anyway, so this is not a problem. |
1087 |
|
|
1088 |
|
\begin{comment} |
1089 |
|
If a malicious behaviour results in an outcome that can also be |
1090 |
|
achieved following the normal protocol with different parameters, |
1091 |
|
then this not a problem at all. |
1092 |
|
\end{comment} |
1093 |
|
|
1094 |
|
The client could also try to create a reference container for $D$ and |
1095 |
|
then not tell $D$ about it. However, a reference container should not |
1096 |
|
consume a lot of resources in the server, and all such resources |
1097 |
|
should be attributed to $C$. When $C$ dies eventually, the server |
1098 |
|
will clean up any such pending containers when the task death |
1099 |
|
notification is processed. |
1100 |
|
|
1101 |
|
The same argument holds when $C$ leaves out the call to |
1102 |
|
\verb/cap_ref_cont_destroy/. |
1103 |
|
|
1104 |
|
The client $C$ could also provide wrong information to $D$. It could |
1105 |
|
supply a wrong server thread ID $T$. It could supply a wrong |
1106 |
|
reference container ID $R$. If $D$ does not trust $C$ and expects a |
1107 |
|
capability implemented by some specific trusted server, it will verify |
1108 |
|
the thread ID numerically and reject it if it does not match. The |
1109 |
|
reference container ID will be verified by the server, and it will |
1110 |
|
only be accepted if the reference container was created by the client |
1111 |
|
task $C$. Thus, the only wrong reference container IDs that the |
1112 |
|
client $C$ could use to not provoke an error message from the server |
1113 |
|
(which then lead $D$ to abort the operation) would be a reference |
1114 |
|
container that it created itself in the first place. However, $C$ |
1115 |
|
already is frree to send $D$ any reference container it created. |
1116 |
|
|
1117 |
|
\begin{comment} |
1118 |
|
Again $C$ can not achieve anything it could not achieve by just |
1119 |
|
following the protocol as well. If $C$ tries to use the same |
1120 |
|
reference container with several RPCs in $D$, one of them would |
1121 |
|
succeed and the others would fail, hurting only $C$. |
1122 |
|
|
1123 |
|
If $D$ does trust $C$, then it can not protect against malicious |
1124 |
|
behaviour by $C$. |
1125 |
|
\end{comment} |
1126 |
|
|
1127 |
|
To summarize the result so far: $C$ can provide wrong data in the |
1128 |
|
operations it does, but it can not achieve anything this way that it |
1129 |
|
could not achieve by just following the protocol. In most cases the |
1130 |
|
operation would just fail. If it leaves out some operations, trying |
1131 |
|
to provoke resource leaks in the server, it will only hurt itself (as |
1132 |
|
the reference container is strictly associated with $C$ until the |
1133 |
|
reference is accepted by $D$). |
1134 |
|
|
1135 |
|
\begin{comment} |
1136 |
|
For optimum performance, the server should be able to keep the |
1137 |
|
information about the capabilities and reference containers a client |
1138 |
|
holds on memory that is allocated on the clients behalf. |
1139 |
|
|
1140 |
|
It might also use some type of quota system. |
1141 |
|
\end{comment} |
1142 |
|
|
1143 |
|
Another attack that $C$ can attempt is to deny a service that $S$ and |
1144 |
|
$D$ are expecting of it. Beside not doing one or more of the RPCs, |
1145 |
|
this is in particular holding the task info caps for the time span as |
1146 |
|
described in the protocol. Of course, this can only be potentially |
1147 |
|
dangerous in combination with a task death. If $C$ does not hold the |
1148 |
|
server task info capability, then an imposter of $S$ could trick $D$ |
1149 |
|
into using the imposter as the server. However, this is only possible |
1150 |
|
if $D$ already trusts $C$. Otherwise it would only allow servers that |
1151 |
|
it already trusts, and it would always hold task info caps to such |
1152 |
|
trusted servers when making the decision that it trusts them. |
1153 |
|
However, if $D$ trusts $C$, it can not protect against $C$ being |
1154 |
|
malicious. |
1155 |
|
|
1156 |
|
\begin{comment} |
1157 |
|
If $D$ does not trust $C$, it should only ever compare the task ID |
1158 |
|
of the server thread against trusted servers it has a task info cap |
1159 |
|
for. It must not rely on $C$ doing that for $D$. |
1160 |
|
|
1161 |
|
However, if $D$ does trust $C$, it can rely on $C$ holding the |
1162 |
|
server task info cap until it got its own. Thus, the task ID of $C$ |
1163 |
|
can be used as the constraint when acquiring the task info cap in |
1164 |
|
the protocol. |
1165 |
|
\end{comment} |
1166 |
|
|
1167 |
|
If $C$ does not hold the task info cap of $D$, and $D$ dies before the |
1168 |
|
server acquires its task info cap for $D$, it might get a task info |
1169 |
|
cap for an imposter of $D$. But if the client wants to achieve that, |
1170 |
|
it could just follow the protocol with the imposter as the destination |
1171 |
|
task. |
1172 |
|
|
1173 |
|
\paragraph{The destination task $D$ is malicious} |
1174 |
|
|
1175 |
|
The destination task has not as many possibilities as $C$ to attack |
1176 |
|
the protocol. This is because it is trusted by $C$. So the only |
1177 |
|
participant that $D$ can try to attack is the server $S$. But the |
1178 |
|
server $S$ does not rely on any action by $D$. $D$ does not hold any |
1179 |
|
task info caps for $S$. The only operation it does is an RPC to $S$ |
1180 |
|
accepting the capability, and if it omits that it will just not get |
1181 |
|
the capability (the reference will be cleaned up by $C$ or by the |
1182 |
|
server when $C$ dies). |
1183 |
|
|
1184 |
|
The only thing that $D$ could try is to provide false information in |
1185 |
|
the \verb/cap_ref_cont_accept/ RPC. The information in that RPC is |
1186 |
|
the task ID of the client $C$ and the reference container ID $R$. The |
1187 |
|
server will verify that the client $C$ has previously created a |
1188 |
|
reference container with the ID $R$ that is destined for $D$. So $D$ |
1189 |
|
will only be able to accept references that it is granted access to. |
1190 |
|
So it can not achieve anything that it could not achieve by following |
1191 |
|
the protocol (possibly the protocol with another client). If $D$ |
1192 |
|
accepts capabilities from other transactions outside of the protocol, |
1193 |
|
it can only cause other transactions in its own task to fail. |
1194 |
|
|
1195 |
|
\begin{comment} |
1196 |
|
If you can do something wrong and harm yourself that way, then this |
1197 |
|
is called ``shooting yourself in your foot''. |
1198 |
|
|
1199 |
|
The destination task $D$ is welcome to shoot itself in its foot. |
1200 |
|
\end{comment} |
1201 |
|
|
1202 |
|
\paragraph{The client $C$ and the destination task $D$ are malicious} |
1203 |
|
|
1204 |
|
The final question we want to raise is what can happen if the client |
1205 |
|
$C$ and the destination task $D$ are malicious. Can $C$ and $D$ |
1206 |
|
cooperate and attacking $S$ in a way that $C$ or $D$ alone could not? |
1207 |
|
|
1208 |
|
In the above analysis, there is no place where we assume any specific |
1209 |
|
behaviour of $D$ to help $S$ in preventing an attack on $S$. There is |
1210 |
|
only one place where we make an assumption for $C$ in the analysis of |
1211 |
|
a malicious $D$. If $D$ does not accept a reference container, we |
1212 |
|
said that $C$ would clean it up by calling |
1213 |
|
\verb/cap_ref_cont_destroy/. So we have to look at what would happen |
1214 |
|
if $C$ were not to do that. |
1215 |
|
|
1216 |
|
Luckily, we covered this case already. It is identical to the case |
1217 |
|
where $C$ does not even tell $D$ about the reference container and |
1218 |
|
just do nothing. In this case, as said before, the server will |
1219 |
|
eventually release the reference container when $C$ dies. Before |
1220 |
|
that, it only occupies resources in the server that are associated |
1221 |
|
with $C$. |
1222 |
|
|
1223 |
|
This analysis is sketchy in parts, but it covers a broad range of |
1224 |
|
possible attacks. For example, all possible and relevant combinations |
1225 |
|
of task deaths and malicious tasks are covered. Although by no means |
1226 |
|
complete, it can give us some confidence about the rightness of the |
1227 |
|
protocol. It also provides a good set of test cases that you can test |
1228 |
|
your own protocols, and improvements to the above protocol against. |
1229 |
|
|
|
Access to task info capabilities can be open to everyone. The above |
|
|
strategies to prevent tasks from allocating too many of them for too |
|
|
long work even if access to task info capabilities is given out |
|
|
without any preconditions, and there is no real incentive other than |
|
|
those above for a task to not pass on a task info capability to any |
|
|
interested task anyway. Allowing every task to create task info |
|
|
capabilities for other tasks simplifies the protocols involved and |
|
|
allows for some optimizations. |
|
1230 |
|
|
1231 |
|
|
1232 |
\subsection{Synchronous IPC} |
\subsection{Synchronous IPC} |
1238 |
asynchronous IPC is assumed. These must be replaced with different |
asynchronous IPC is assumed. These must be replaced with different |
1239 |
strategies. One example is the implementation of select() in the GNU |
strategies. One example is the implementation of select() in the GNU |
1240 |
C library. |
C library. |
1241 |
|
|
1242 |
|
\begin{comment} |
1243 |
|
A naive implementation would use one thread per capability to select |
1244 |
|
on. A better one would combine all capabilities implemented by the |
1245 |
|
same server in one array and use one thread per server. |
1246 |
|
|
1247 |
|
A more complex scheme might let the server process select() calls |
1248 |
|
asynchronously and report the result back via notifications. |
1249 |
|
\end{comment} |
1250 |
|
|
1251 |
In other cases the Hurd receives the reply asynchronously from sending |
In other cases the Hurd receives the reply asynchronously from sending |
1252 |
the message. This works fine in Mach, because send-once rights are |
the message. This works fine in Mach, because send-once rights are |
1253 |
used as reply ports and Mach guarantees to deliver the reply message, |
used as reply ports and Mach guarantees to deliver the reply message, |
1255 |
such places need to be rewritten in a different way (for example using |
such places need to be rewritten in a different way (for example using |
1256 |
extra threads). |
extra threads). |
1257 |
|
|
1258 |
|
|
1259 |
\subsection{Notifications} |
\subsection{Notifications} |
1260 |
|
|
1261 |
Notifications to untrusted tasks happens frequently. One case is |
Notifications to untrusted tasks happen frequently. One case is |
1262 |
object death notifications, in particular task death notifications. |
object death notifications, in particular task death notifications. |
1263 |
Other cases might be select() or notifications of changes to the |
Other cases might be select() or notifications of changes to the |
1264 |
filesystem. |
filesystem. |
1265 |
|
|
1266 |
The console uses notifications to broadcast change events to the |
The console uses notifications to broadcast change events to the |
1267 |
console content, but it also uses shared memory to broadcast the |
console content, but it also uses shared memory to broadcast the |
1268 |
actual data, so not all notifications need to be received for |
actual data, so not all notifications need to be received for |
1298 |
notified of pending notifications. Then the clients can poll the |
notified of pending notifications. Then the clients can poll the |
1299 |
notifications from the servers. |
notifications from the servers. |
1300 |
|
|
|
The whole issue of notifications requires more thoughtful analysis. |
|
1301 |
|
|
1302 |
\subsection{Capabilities} |
\section{Threads and Tasks} |
1303 |
|
|
1304 |
Capabilities will be the building stones of the Hurd system. Servers |
The Hurd will encode the task ID in the version part of the L4 thread |
1305 |
will provide capabilities to clients. Clients can invoke messages on |
ID. The version part can only be changed by the privileged system |
1306 |
the capabilities, which are then processed by the server providing the |
code, so it is protected by the kernel. This allows recipients of a |
1307 |
capability. A capability will be normally associated with an object |
message to quickly determine the task from the sender's thread ID. |
|
at the server side (for example an opened file). |
|
|
|
|
|
The low level interface will use client capability IDs that are local |
|
|
to each client. If a client gets the same capability through two |
|
|
different ways at the same time, well-behaving servers will provide |
|
|
the same local ID both times. This allows a client to compare local |
|
|
IDs numerically to establish identity within capabilities provided by |
|
|
a single server.. |
|
|
|
|
|
Clients will be able to copy capabilities to other tasks. This will |
|
|
be possible without requiring mutual trust between the clients and the |
|
|
server (the only trust requirements are that the clients trust the |
|
|
server and that the sender of a capability trusts the receiver). |
|
|
|
|
|
The straightforward protocol to move a capability from one client C1 |
|
|
to another client C2 is that the client C1 sends a request to the |
|
|
server S to create a transitional object, a reference container |
|
|
destined for client C2. After receiving the identifier for this |
|
|
object, client C1 sends the information about it to C2. C2 can then |
|
|
send a request to the server S to complete the transition, and reply |
|
|
to C1 to allow it to synchronize with the completion of the operation |
|
|
and destroy the transitional object. |
|
|
|
|
|
There are some obvious and not-so-obvious properties of this basic |
|
|
protocol. For example, C1 can not hide references to objects in other |
|
|
tasks, because the receiver has to explicitely accept the reference. |
|
|
If C1 were to die before C2 can accept the reference (or if C2 does |
|
|
reject to accept the handle), the server S would destroy the |
|
|
transitional object. On the other hand, C1 does not need to rely on |
|
|
C2 to accept the reference, as it will always destroy it afterwards. |
|
|
|
|
|
This basic protocol is not enough to provide secure capability |
|
|
transfer on L4, though, as at any time a participant could die, and |
|
|
there is the danger of another task reusing that participants task and |
|
|
thread IDs. Such an imposter can then gain access to capabilities it |
|
|
would normally not allowed to get. To prevent this, task info |
|
|
capabilities have to be acquired by all participants to ensure that |
|
|
the task IDs of the others are not reused in the whole process. This |
|
|
greatly increases the complexity of the protocol. |
|
|
|
|
|
The exact syntax of such a protocol depend on the actual interfaces. |
|
|
But here is a rough overview. The starting condition is that C1 has a |
|
|
capability implemented in S, and a capability implemented in C2. C1 |
|
|
will send the capability implemented in S as part of a message invoked |
|
|
on the capability implemented in C2. Because C1 and S, as well as C1 |
|
|
and C2, are already communicating, C1 has task info capabilities for S |
|
|
and C2, S has a task info capability for C1, and C2 has a task info |
|
|
capability for S. |
|
1308 |
|
|
1309 |
\begin{enumerate} |
Task IDs will not be reused as long as there are still tasks that |
1310 |
\item C1 sends a request to S to create a transitional object |
might actively communicate with the (now destroyed) task. Task info |
1311 |
(reference container) destined for C2. |
capabilities provided by the task server can be used for that. The |
1312 |
\item Before replying, S acquires a task info capability for C2 if it |
task info capability will also receive the task death notification (as |
1313 |
doesn't have any already. It also must check if C1 is still alive |
a normap capability death notification). The task server will reuse a |
1314 |
after that (this can be done by the task server along with creating |
task ID only when all task info capabilities for the task with that ID |
1315 |
the task info capability), before entering the container into its |
have been released. |
1316 |
data structures. This prevents that an imposter (of C2) can acquire |
|
1317 |
the capability by guessing the reference container ID before the |
This of course can open a DoS attack. Programs can attempt to acquire |
1318 |
server can receive and process the task death notifications for C1. |
task info capabilities and never release them. Several strategies can |
1319 |
\item Then the server replies to C1 with the reference container ID. |
be applied to compensate that: The task server can automatically time |
1320 |
The task info capability will stay with the reference container, and |
out task info capability references to dead tasks. The proc server |
1321 |
both will be associated with C1. If C1 dies now before C2 accepts |
can show dead task IDs with task info capability references as some |
1322 |
the capability, both the reference and the task info capability will |
variant of zombie tasks, and provide a way to list all tasks |
1323 |
be destroyed. |
preventing the task ID from being reused, allowing the system |
1324 |
\item C1 sends a request to C2 with the reference container ID and |
administrator to identify malicious or faulty users. Task ID |
1325 |
other necessary information (like the server thread ID). |
references can be taken into account in quota restrictions, to |
1326 |
\item C2 looks at the server thread ID, and if it wants to accept a |
encourage a user to release them when they are not needed anymore (in |
1327 |
capability implemented by this server (in other words: if it trusts |
particular, a user holding a task ID reference to a dead task could be |
1328 |
that server), it acquires a task info capability for the server task |
punished with the same costs as for an additional normal task owned by |
1329 |
if it doesn't have any already. It then must check if C1 is still |
the user). Another idea is to not allow any task to allocate more |
1330 |
alive (this can be done by the task server along with creating the |
task info capabilities than there are live tasks in the system, plus |
1331 |
task info capability), because otherwise there might already be an |
some slack. This provides a high incentive for tasks to release their |
1332 |
imposter (of S). |
info caps (and if they get an error, they could block until their |
1333 |
\item Now C2 can send a request to S to accept the capability from C1. |
notification system has processed the task death notification and |
1334 |
\item The server will check that there is a reference container for C2 |
released the reference, and try again). |
1335 |
provided by C1. It will then install the reference as a proper |
|
1336 |
reference for the capability owned by C2. For this, it will also |
Access to task info capabilities can be open to everyone. The above |
1337 |
install the task info capability. If now C1 dies, the reference |
strategies to prevent tasks from allocating too many of them for too |
1338 |
container will be empty and C2 will keep its capability reference. |
long work even if access to task info capabilities is given out |
1339 |
\item The server replies to C2, returning the capability ID for C2. |
without any preconditions, and there is no real incentive other than |
1340 |
\item C2 can now return to C1, indicating success. |
those above for a task to not pass on a task info capability to any |
1341 |
\item C1 can now destroy the transitional objectq, and optionally |
interested task anyway. Allowing every task to create task info |
1342 |
deallocate its own reference to the capability. |
capabilities for other tasks simplifies the protocols involved and |
1343 |
\end{enumerate} |
allows for some optimizations. |
1344 |
|
|
1345 |
Each step is necessary, and the order is peculiar. If various things |
|
1346 |
go wrong, all behaving participants in this protocol can properly |
\subsection{Proxy Task Server} |
1347 |
clean up their state and resources without being harmed or tricked |
\label{proxytaskserver} |
1348 |
into trusting a task they don't want to trust. |
|
1349 |
|
The task server can be safely proxied, and the users of such a proxy |
1350 |
There will be other protocols, for example to return new capabilities |
task server can use it like the real task server, even though |
1351 |
to the same server in a server reply (which is easy to do), and to |
capabilities work a bit different for the task server than for other |
1352 |
receive capabilities implemented by other servers from a server (which |
servers. |
1353 |
can be done by creating empty reference containers in the client |
|
1354 |
before sending the request). |
The problem exists because the proxy task server would hold the real |
1355 |
|
task info capabilities for the task info capabilities that it provides |
1356 |
|
to the proxied task. So if the proxy task server dies, all such task |
1357 |
|
info capabilities would be released, and the tasks using the proxy |
1358 |
|
task server would become insecure and open to attacks by imposters. |
1359 |
|
|
1360 |
|
However, this is not really a problem, because the proxy task server |
1361 |
|
will also provide proxy objects for all task control capabilities. So |
1362 |
|
it will be the only task which holds task control capabilities for the |
1363 |
|
tasks that use it. When the proxy task server dies, all tasks that |
1364 |
|
were created with it will be destroyed when these tak control |
1365 |
|
capabilities are released. The proxy task server is a vital system |
1366 |
|
component for the tasks that use it, just as the real task server is a |
1367 |
|
vital system component for the whole system. |
1368 |
|
|
1369 |
|
|
1370 |
\section{Virtual Memory Management} |
\section{Virtual Memory Management} |
1441 |
to be destroyed by exec() anyway. There is a lot of Hurd specific |
to be destroyed by exec() anyway. There is a lot of Hurd specific |
1442 |
state associated with a task (capabilities, for example), but it is |
state associated with a task (capabilities, for example), but it is |
1443 |
difficult to preserve that. There are security concerns, because |
difficult to preserve that. There are security concerns, because |
1444 |
POSIX programs don't know about Hurd features like capabilities, so |
POSIX programs do not know about Hurd features like capabilities, so |
1445 |
inheriting all capabilities across exec() seems dangerous. There are |
inheriting all capabilities across exec() seems dangerous. There are |
1446 |
also implementation obstacles, because only local threads can |
also implementation obstacles, because only local threads can |
1447 |
manipulate the virtual memory mappings, and there is a lot of local |
manipulate the virtual memory mappings, and there is a lot of local |
1516 |
|
|
1517 |
|
|
1518 |
\section{Authentication} |
\section{Authentication} |
1519 |
|
\label{auth} |
1520 |
|
|
1521 |
The auth server gives out auth objects that contain zero or more of |
The auth server gives out auth objects that contain zero or more of |
1522 |
effective user IDs, available user IDs, effective group IDs and |
effective user IDs, available user IDs, effective group IDs and |
1537 |
unless they were given the passport object by that task. |
unless they were given the passport object by that task. |
1538 |
|
|
1539 |
|
|
|
|
|
1540 |
\section{Unix Domain Sockets and Pipes} |
\section{Unix Domain Sockets and Pipes} |
1541 |
|
|
1542 |
In the Hurd on Mach, there was a global pflocal server that provided |
In the Hurd on Mach, there was a global pflocal server that provided |
1561 |
an active translator must be installed in the node that redirects any |
an active translator must be installed in the node that redirects any |
1562 |
other users to the right pflocal server implementing this fifo. This |
other users to the right pflocal server implementing this fifo. This |
1563 |
is asymmetrical in that the first user to access a fifo will implement |
is asymmetrical in that the first user to access a fifo will implement |
1564 |
it, and thus pay the costs for it. But it doesn't seem to cause any |
it, and thus pay the costs for it. But it does not seem to cause any |
1565 |
particular problems in implementing the POSIX semantics. |
particular problems in implementing the POSIX semantics. |
1566 |
|
|
1567 |
The GNU C library can contact ~/servers/socket/pflocal to implement |
The GNU C library can contact ~/servers/socket/pflocal to implement |
1568 |
socketpair, or start a pflocal server for this task's exclusive use if |
socketpair, or start a pflocal server for this task's exclusive use if |
1569 |
that node doesn't exist. |
that node does not exist. |
1570 |
|
|
1571 |
All this are optimizations: It should work to have one pflocal process |
All this are optimizations: It should work to have one pflocal process |
1572 |
for each socketpair. However, performance should be better with a |
for each socketpair. However, performance should be better with a |
1575 |
|
|
1576 |
\section{Filesystem Translators} |
\section{Filesystem Translators} |
1577 |
|
|
1578 |
|
\label{xfslookup} |
1579 |
|
|
1580 |
The Hurd has the ability to let users mount filesystems and other |
The Hurd has the ability to let users mount filesystems and other |
1581 |
servers providing a filesystem-like interface. Such filesystem |
servers providing a filesystem-like interface. Such filesystem |
1582 |
servers are called translators. In the Hurd on GNU Mach, the parent |
servers are called translators. In the Hurd on GNU Mach, the parent |
1606 |
authentication capability from the parent filesystem, and receiving a |
authentication capability from the parent filesystem, and receiving a |
1607 |
root directory capability in exchange). |
root directory capability in exchange). |
1608 |
|
|
1609 |
|
\begin{comment} |
1610 |
|
There is a race here. If the child filesystem dies and the parent |
1611 |
|
filesystem processes the task death notification and releases the |
1612 |
|
task info cap for the child before the user acquires its own task |
1613 |
|
info cap for the child, then an imposter might be able to pretend to |
1614 |
|
be the child filesystem for the client. |
1615 |
|
|
1616 |
|
This race can only be avoided by a more complex protocol: |
1617 |
|
|
1618 |
|
Variant 1: The user has to acquire the task info cap for the child |
1619 |
|
fs, and then it has to perform the lookup again. If then the thread |
1620 |
|
ID is for the task it got the task ID for in advance, it can go on. |
1621 |
|
If not, it has to retry. This is not so good because a directory |
1622 |
|
lookup is usually an expensive operation. However, it has the |
1623 |
|
advantage of only slowing down the rare case. |
1624 |
|
|
1625 |
|
Variant 2: The client creates an empty reference container in the |
1626 |
|
task server, which can then be used by the server to fill in a |
1627 |
|
reference to the child's task ID. However, the client has to create |
1628 |
|
and destroy such a container for every filesystem where it excepts |
1629 |
|
it could be redirected to another (that means: for all filesystems |
1630 |
|
for which it does not use \verb/O_NOTRANS/). This is quite an |
1631 |
|
overhead to the common case. |
1632 |
|
\end{comment} |
1633 |
|
|
1634 |
The actual creation of the child filesystem can be performed much like |
The actual creation of the child filesystem can be performed much like |
1635 |
a suid exec, just without any client to follow up with further |
a suid exec, just without any client to follow up with further |
1636 |
capabilities and startup info. The only problem that remains is how |
capabilities and startup info. The only problem that remains is how |